2021年10月23日补充。
线程不安全类型条件 == 共享 || 可变
共享:线程间共享,Java的堆是共享的,对象在堆上创建,所以一般对象都是共享的。(例外:JDK里的LocalVariabl; Flutter 单线程模型,堆不进行共享)
可变:(mutable),先说什么是 不可变:例如final修饰,或者 String。只要一改动,就重新 赋值,而不是直接改变。
线程原子性实用分类:
看《JAVA并发编程实践》关于线程不安全类型,有个很有趣且实用的分类:
分类:
1.检查执行(check then execute)
典型的就是单例模式(要求全局只有一个对象)
public static synchronized Instance getInstance(){
if(instance == null){
instance = new Instance();
}
return instance;
}
如果不加synchronized,且 instance为空时:
在instance==null这个检查点上A线程停止(还没有执行到instance = new Instance()),B线程运行,A、B都会判断为true,然会返回两个对象,违反单例模式。
2.读改写(read change write)
这个典型的就是生产者-消费者模式了。即使方法内是原子性操作,可两个方法在两个线程下混合调用也可能有问题。
如使用Vector线程安全类(读操作,写操作本身是原子性操作不会出问题),a线程调用aA()方法读最后元素,b线程调用bB()方法删除最后元素。
aA()方法:得到Vector的size--》读取index == size-1的元素。
bB()方法:得到Vector的size--》删除index == size-1的元素,size--
条件:Vector对象假设size == 5.
执行过程:a线程得到size等于5,线程暂停运行-》
b线程运行,得到size等于5 --》
删除index == 4的元素,size--(size == 4,最后元素index == 3),b线程终止-》
a线程读取index == 4,索引越界报错。
这个时候就用 synchronized(lock){},给aA()、bB()方法内部加锁,lock是随便一个对象了。保证aA()、bB()方法调用互斥(即一个执行,另一个绝对不能执行)。
总结:
简单来说 就是原子性操作的两种情景。上文说的那本书也不错,可以了解很多java并发包下的知识,简化并发编程。