11. 原子性
大约 2 分钟
一、一个小测验
并发情况下,下面两个类是否线程安全?
public class Counter {
private int count;
public void increase() {
count = count + 1;
}
public int getCount() {
return count;
}
}
public class Basket {
//Vector是线程安全的集合
Vector<Fruit> v = new Vector<Fruit>();
public void put(Fruit fruit) {
v.add(fruit);
}
public Fruit tackOut() throws Exception {
if (v.size() > 0) {
return v.remove(0);
} else {
throw new Exception("There is no fruit in basket.");
}
}
}
答案:以上两个类在并发情况下均不能保证线程安全。
二、线程安全和原子性
线程安全:当多个线程访问某个类时,这类始终都能表现出正确的行为,那么这个类是线程安全的。
原子性:一个或者多个操作在CPU执行的过程中不被中断的特性称为原子性。
而以上两个类不能保证操作原子性导致不能表现正确的行为,因此都不是线程安全的。
1.在类Counter中,以下代码实际执行了三个指令:
count = count + 1;
在多线程并发情况下由于线程切换会导致计算结果不正确。
注:图片参考Java并发编程实战
2.在类中的如下代码不是原子的,可能会导致抛出空指针异常和预期不符:
if (v.size() > 0) {
return v.remove(0);
}
三、修正
原子性可以通过加锁来解决,修正如下:
public class Counter {
private int count;
public synchronized void increase() {
count = count + 1;
}
/** 原子操作可不加同步关键字 */
public int getCount() {
return count;
}
}
public class Basket {
Vector<Fruit> v = new Vector<Fruit>();
/** 原子操作可不加同步关键字 */
public void put(Fruit fruit) {
v.add(fruit);
}
/** 加上synchronized关键字 */
public synchronized Fruit tackOut() throws Exception {
if (v.size() > 0) {
return v.remove(0);
} else {
throw new Exception("There is no fruit in basket.");
}
}
}