J.U.C
J.U.C
前言
首先我们要编写一些代码的例子,以便更好测试和理解哪个操作是线程安全,那些操作不是线程安全的。
1 | 4j |
该代码就是利用多线程执行 count++
操作!
很明显执行后的结构肯定是小于八千的,主要原因是count++这个操作并非原子性。多线程会影响其count的取值。
Atomic 包
Atomic包里面有很多基本类型的线程安全类,当我们把上诉代码中的Int缓存AtomicInteger此代码就是线程安全的。
看下源码
1 | public final int getAndIncrement() { |
关键代码是native修饰,并不是由java实现。但是从这简单的代码,我第一次看到就猜到了这不就是乐观锁么。事实也确实如此。compareAndSwapInt 方法就是在作比较成功就交换的动作。一只不断尝试。所以Atomic包下的类基本就是利用这种CAS无锁操作。来实现并发支持。理论上这种循环都是很快就能够完成的。比锁的效率要高。
不建议在有大量更新的情况下使用。更多的是作为一些状态的标记来使用。
其他的Atomic包下的类有:
- AtomicLong LongAdder
- ActomicBoolean
- ActomicReference
- ActomicIntegerFieldUpdate 更新某个类的字段 字段必须是volatile
- ActomicStampReference CAS的ABA的问题
就先不多做介绍了。
并发集合
我们都知道我们常用的ArrayList、LinkedList都不是线程安全的,并发情况下会出现数据BUG。
那有哪些List是线程安全的了?
Vector
vector并不是J.U.C下的包。但是他确实是一个线程安全的集合和ArrayList一样,底层都是由数组实现。不同的是它的基本所有的方法都加了synchronized加锁,以保证线程的安全性。
Stack
stack也不是J.U.C下的包。他是一个线程安全的集合。和Vector一样也是加了synchronized保证线程安全性。stack是一个算法中常用的模型。基于FILO原则。
CopyOnWriteArrayList
J.U.C下的并发类,简单看下它的部分内部源码
1 | public boolean add(E e) { |
重点在add方法,首先利用ReentrantLock这种独占锁保持线程安全。在add操作时,先copy一份数组出来,更新后在写会原数据。这种方式保证了读取的效率。读数据不会受写数据的影响。但是这中缺点也比较明显。
- 保证了最终一致性,但是实时性比较差
- 由于每次更新都要copy一次整个数据,对于数据量偏大的数据容器引起YGC、FGC的操作。
所以这个比较适合数据量不大,读多写少,而且实时性安全不是很严格的情况下。该数据实际使用的情况不多。
CopyOnWriteArraySet
底层使用了CopyOnWriteArrayList来实现,唯一的区别就是List和Set的区别。
ConcurrentSkipListMap、ConcurrentSkipListSet
ConcurrentSkipListMap底层采用了一种跳表的数据形式,实际是一种链表。底层通过CAS的方式保证了线程安全性。 ConcurrentSkipListSet底层使用了ConcurrentSkipListMap来实现
注意两者都不支持null的处理,同时对于addAll等批量操作,不能保证安全性。只能保证单数据操作的安全性
ConcurrentHashMap
用的最多的并发类,可以查看其他文档。
其他
Collections.synchronizedXXX:collection、List、Set、Map
我们可以通过Collections工具类来直接生成一个线程安全的集合,实现比较简单,通过synchronized关键字来保证线程安全
原文作者: duteliang
原文链接: http://yoursite.com/2019/05/20/javabase/J.U.C/
版权声明: 转载请注明出处(必须保留原文作者署名原文链接)