线程安全
线程安全问题是一个相对专业的编程问题,本章节只提供给有需要的用户。
引用维基百科的解释:
线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。
在 Auto.js 中,线程间变量在符合 JavaScript 变量作用域规则的前提下是共享的,例如全局变量在所有线程都能访问,并且保证他们在所有线程的可见性。但是,不保证任何操作的原子性。例如经典的自增"i++"将不是原子性操作。
Rhino 和 Auto.js 提供了一些简单的设施来解决简单的线程安全问题,如锁threads.lock()
, 函数同步锁sync()
, 整数原子变量threads.atomic()
等。
例如,对于多线程共享下的整数的自增操作(自增操作会导致问题,是因为自增操作实际上为i = i + 1
,也就是先读取 i 的值, 把他加 1, 再赋值给 i, 如果两个线程同时进行自增操作,可能出现 i 的值只增加了 1 的情况),应该使用threads.atomic()
函数来新建一个整数原子变量,或者使用锁threads.lock()
来保证操作的原子性,或者用sync()
来增加同步锁。
线程不安全的代码如下:
1var i = 0;
2threads.start(function () {
3 while (true) {
4 log(i++);
5 }
6});
7while (true) {
8 log(i++);
9}
此段代码运行后打开日志,可以看到日志中有重复的值出现。
使用threads.atomic()
的线程安全的代码如下:
1//atomic返回的对象保证了自增的原子性
2var i = threads.atomic();
3threads.start(function () {
4 while (true) {
5 log(i.getAndIncrement());
6 }
7});
8while (true) {
9 log(i.getAndIncrement());
10}
或者:
1//锁保证了操作的原子性
2var lock = threads.lock();
3var i = 0;
4threads.start(function () {
5 while (true) {
6 lock.lock();
7 log(i++);
8 lock.unlock();
9 }
10});
11while (true) {
12 lock.lock();
13 log(i++);
14 lock.unlock();
15}
或者:
1//sync函数会把里面的函数加上同步锁,使得在同一时刻最多只能有一个线程执行这个函数
2var i = 0;
3var getAndIncrement = sync(function () {
4 return i++;
5});
6threads.start(function () {
7 while (true) {
8 log(getAndIncrement());
9 }
10});
11while (true) {
12 log(getAndIncrement());
13}
另外,数组 Array 不是线程安全的,如果有这种复杂的需求,请用 Android 和 Java 相关 API 来实现。例如CopyOnWriteList
, Vector
等都是代替数组的线程安全的类,用于不同的场景。例如:
1var nums = new java.util.Vector();
2nums.add(123);
3nums.add(456);
4toast("长度为" + nums.size());
5toast("第一个元素为" + nums.get(0));
但很明显的是,这些类不像数组那样简便易用,也不能使用诸如slice()
之类的方便的函数。在未来可能会加入线程安全的数组来解决这个问题。当然您也可以为每个数组的操作加锁来解决线程安全问题:
1var nums = [];
2var numsLock = threads.lock();
3threads.start(function () {
4 //向数组添加元素123
5 numsLock.lock();
6 nums.push(123);
7 log("线程: %s, 数组: %s", threads.currentThread(), nums);
8 numsLock.unlock();
9});
10
11threads.start(function () {
12 //向数组添加元素456
13 numsLock.lock();
14 nums.push(456);
15 log("线程: %s, 数组: %s", threads.currentThread(), nums);
16 numsLock.unlock();
17});
18
19//删除数组最后一个元素
20numsLock.lock();
21nums.pop();
22log("线程: %s, 数组: %s", threads.currentThread(), nums);
23numsLock.unlock();
sync(func)
func
{Function} 函数
- 返回 {Function}
给函数 func 加上同步锁并作为一个新函数返回。
1var i = 0;
2function add(x) {
3 i += x;
4}
5
6var syncAdd = sync(add);
7syncAdd(10);
8toast(i);