四、java中的线程安全等级
不可变:
可以是基本类型的final;可以是final对象,但对象的行为不会对其状态产生任何影响,比如String的subString就是new一个String对象各种Number类型如BigInteger和BigDecimal等大数据类型都是不可变的,但是同为Number子类型的AtomicInteger和AtomicLong则并非不可变。原因与它里面状态对象是unsafe对象有关,所做的操作都是CAS操作,可以保证原子性。
绝对线程安全:
不管运行时环境如何,调用者都不需要任何额外的同步措施。
相对线程安全:
这是我们通常意义上的线程安全。需要保证对象单独的操作是线程安全的。比如Vector,HashTable,synchronizedCollection包装集合等。
线程兼容:
对象本身不是线程安全的,但可以通过同步手段实现。一般我们说的不是线程安全的,绝大多数是指这个。比如ArrayList,HashMap等。
线程对立:
不管调用端是否采用了同步的措施,都无法在并发中使用的代码。
五、线程安全的实现方式
要实现线程安全一般至少需要两个特性:原子性和可见性
1)使用synchronize:它本具有原子性和可见性的,所以如果使用了synchronize修饰的操作,那么就自带了可见性,synchronized使用悲观锁来实现线程安全
2)使用原子类代替基本数据类型,原子类是使用乐观锁来实现线程安全,多线程环境下执行a++,可以使用AtomicInteger类incrementAndGet()方法实现,同样是使用了volatile来保证可见性;使用Unsafe调用native本地方法CAS,CAS采用总线加锁或缓存加锁方式来保证原子性。
3 ) 使用volatile关键字,volatile不一定就有原子性,比如用volatile修饰的变量进行++或者--操作(num++),我们需要让volatile修饰的变量需要具有原子性,那么我们一般可以设置在boolean类型变量上,如下
volatile boolean tag = true; 线程1 while(tag){}; 线程2 while(tag){};
如果有变量自增或者自减,我们可以使用原子类(AtomicInteger)
4)使用ThreadLocal对各个线程进行隔离
可以参考我的这篇博客 :Java之ThreaLocal
5)我们还可以用其他的锁,比如重入锁(ReentrantLock) 保证线程安全
6)我们还可以用 临界区、互斥量、信号量 保证线程安全
参考文献:
https://developer.51cto.com/art/201910/605093.htm
https://www.open-open.com/lib/view/open1459412319988.html
https://www.iteye.com/blog/smallbug-vip-2275743
https://juejin.im/post/5c936018f265da60ec281bcb
https://blog.csdn.net/jingzi123456789/article/details/78004074