Concurrent包综述 by Seven
Concurrent包下好多很处理多线程高并发的类,包括锁,队列,工具类和原子类等等。这个系列会讲解一些常见类的源码。
先分类一下总写一下。后面会详细介绍每一种典形的类。
-
Lock
-
AQS:
abstractQueuedSynchronizer
抽象队列同步器,并发包的核心类,很多类都含有他的实现,本文的重点;两个常见的子类NonfairSync
和FairSync
实现了公平锁和非公平锁。实现有共享锁和独占锁两种。
-
ReentrantLock
,可重入锁,实现了lock ,unlock, tryLock
等方法,内部就是CAS.含有Condition类,可以记录lock含有多个等待队列,从而更细地控制线程的运行。
如:生产者消费者模式;
-
ReentrantReadWriteLock
读写锁,内部integer类型的state变量分成前面16位表示读锁,后面16位表示读锁。公用一个queue,读锁 lock的时候会判断state,确保只能叠加读锁,unlock的时候会改变state的前半部分。写锁lock的时候确保没有读锁,unlock的时候也会释放写锁。 -
LockSupport
一个底层的类,调用Unsafe实现对线程的park和unpark
. -
Condition: 内部是一个双向链表,实现了
public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; /** First node of condition queue. */ private transient Node firstWaiter; /** Last node of condition queue. */ private transient Node lastWaiter;
-
-
BlockingQueue
包下有很多的阻塞队列,阻塞队列比一般队列如PriorotyQueue
多了put和take两个方法,这两个方法在内部容器满了和空了的时候会阻塞。内部实现使用的是一个Condition进行阻塞。- ArrayBlockingQueue:使用数组实现
- LinkedBlockingQueue: 使用队列实现;
- DelayQueue: 按时间排序的队列,最小堆
- SynchroniousQueue: 手把手传递
-
并发包下的工具类:包括Semaphore, CountDownLatch, CyclicBarrier,
Semaphore: 信号量,可以用来控制并发数,限制同时运行的线程的数量;
原理: 共享型CAS, 方法:acquire()获得锁的,release释放锁。没有获得的阻塞。
countDownLatch: 倒计时锁,使用场景:多个线程完成后,统一交给一个线程处理。
原理:共享CAS。 方法:多个线程countdown,为0后,await()的线程启动。
cyclicBarrier: 回环栅栏:多个线程await()全部到齐后,一起出发。
原理:所有线程await,park(),数量–, 为0后,unpark。
-
Aotmic原子类,提供一些原子操作。
包括AtomicInteger, AtomicLong等,
原理:通过CAS操作。
后面的文章中,将在
2_1 讲解AQS源码分析,分析内部的主要三个构建
- state,控制只有一个线程(互斥)或多个线程(共享),
- 双向链表Node, 存储所有的等待线程
- CAS操作,所有的操作都是通过CAS操作的,高效
2_2 讲解共享锁的实现,包括其中一些典型的类
- ReentrantReadWriteLock: state前16位表示读锁,后16位表示写锁
- Semaphore:信号量,acquire()没有获得的进行双向队列等待。
- CountDownLatch: 等待所有线程处理完了,在交给一个线程统一处理;
2_3 讲解Condition和CyclicBarrier源码
- Condition, 内部是一个双向链表的等待队列,con.notify表示将等待队列中的第一个元素移到同步队列中;con.awiat()表示将当前线程从同步队列移动到等待队列中。可以实现生产者消费者。
- CyclicBarrier。 场景:多个线程到达同一位置一起出发。压测。
- 原理:.await()会将线程加入到等待队列中,如果state变为0以后,notifyAll。
3 讲解ThreadLocal线程本地对象
每个线程thread对象里面有一个hashMap, key是ThreadLoacl, value是放进去的值。
然后,一个ThreadLocal对象里面也是一个hashmap,key是thread的id,value是线程对象里的map。
当我们通过local.get一个对象的时候,实际上是通过当前线程的id获取到它的thread的里面hashmap, 然后在获取到里面的值。
其中又涉及到虚引用。
4_0 四种引用:强软弱虚。
软: 放一些对象,档期只有软引用指向它的时候,虚拟机内存不够的时候,便回收他。常用场景:存储一些大的图片做缓存。
弱:虚拟机回收的时候,当只有弱应用存在的时候就回收它。应用场景:底层的架构中的一些内部类存储了一些信息,上层应用指向了它,但是当上层删除的时候,我们希望这个内部类中存储的信息也删除掉。
虚:用于处理堆外内存。
5_0 HashMap和Collection的底层源码
6_0 ThreadPool的底层源码