一、线程之间的通信
1、 等待/唤醒机制;
通过 wait 和 notify 来进行相关的通信(一定条件下的等待和唤醒)(例子:生产者和消费者)
wait(会释放拿到的锁)、notify(随机叫醒一个处于 wait 状态的线程(同一锁对象情况下,先释放了 notify 的锁才能让唤醒的线程获取锁),此时此线程会再拿到锁)
当线程在执行中 wait(); 之后,若要唤醒线程 ,则 需要在同步代码块中给同步线程run方法 调用notify唤醒
使用条件:在同步代码块中使用,notify唤醒的是同一对象下的 wait 的线程;
2、Condition 接口 -- await()、signal() -- ConditionObject 实现类(juc下的AQS的内部类)
Lock lock = new ReentrantLock(); // 创建锁
Condition a = lock.newCondition(); // 创建 Condition,相当于创建了一个条件队列
Condition b = lock.newCondition();
// 在加锁的条件下使用
a.await(); //将指定 condition 所在的方法中的线程 wait
b.signal();//将指定 condition 所在的方法中的线程 notify
2.1、condition 与 wait、notify(notifyAll)
notify:随机唤醒一个同一对象下的线程; notifyAll:唤醒所有同一对象下的线程;
condition.signal:唤醒指定 condition await 的线程
3、join方法 -- 线程加塞
在一个线程中执行另一个线程, thread.join(); // 线程加塞后可让加塞的线程执行完再执行原来线程接下来的代码
joinThread.start(); // 开启线程
joinThread.join(); // 阻塞当前线程,当 joinThread 线程执行完毕(销毁)则唤醒当前线程
// join 源码
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// -------------------------------------
if (millis == 0) {
// 调用 join 的线程在此自旋,循环将 当前线程 wait 0 milliseconds;当 joinThread 线程销毁后,则退出循环
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
// -------------------------------------
}
4、ThreadLocal -- 实现每一个线程都有自己的共享变量
主要解决的就是每个线程绑定自己的值。将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的是有数据。不同线程中的值是可以放入ThreadLocal类中进行保存的
private ThreadLocal<Integer> count = new ThreadLocal<Integer>() { // 创建了一个变量“盒子”,有get、set、remove等方法
protected Integer initialValue() { // 重写返回值方法
return new Integer(0);
};
};
底层靠ThreadLocal的内部类ThreadLocalMap来实现不同线程间的资源存储。
5、CountDownLatch并发工具类 -- 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
- 用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。
-await的是当前线程
// 1、创建一个 CountDownLatch 的实例
CountDownLatch latch = new CountDownLatch(count);
// 2、在其他每个线程中调用一次 latch.countDown()方法 计数
latch.countDown();
// 3、在需要汇总操作前的位置 await 当前线程,当计数线程执行完销毁后,会自动唤醒当前线程
latch.await();
6、CyclicBarrier并发工具类 -- 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
- CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
-await的是其他线程
// 在主线程中创建一个 CyclicBarrier 的实例;条件达到时会唤醒被await 的所有线程并执行 参数线程动作
CyclicBarrier barrier = new CyclicBarrier(10, new Runnable() { // 参数为 (等待的线程数,达到等待数条件时执行的线程动作)
@Override
public void run() {
// 执行动作
}
});
//在其他线程中调用 await,每调用一次都会使线程等待数目条件 +1
barrier.await();
7、Semaphore - 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。
// 1、在主线程中创建一个 Semaphore 的实例,指定可执行的线程数作为参数
Semaphore semaphore = new Semaphore(10);
// 2、在其他线程调用的方法中
// 2.1、开始前用 semaphore 对象调用 acquire 方法
semaphore.acquire();
// 2.2、结束后用 semaphore 对象调用 release 方法
semaphore.release();
- 当不断的有新的线程调用方法时,semaphore 便会阻塞限定数目外的线程,使这些线程进入等待状态;
8、Exchanger -- 可以在对中对元素进行配对和交换的线程的同步点。每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。 Exchanger 可能在应用程序(比如遗传算法和管道设计)中很有用。
-- 获取到另一个线程放入同一个Exchanger对象的 exchange 方法中的 信息
// 1、在主线程中创建一个 Exchanger 的实例
Exchanger<String> exch = new Exchanger<>();
// 2、在其他线程调用的方法中使用同一个 exch 实例来进行通信
String value = exch.exchange(res); // 获取到另一个线程放入 exchange 方法中 res
二、Condition 源码解析
1、创建一个 Condition 实例
1.1、创建一个锁对象
Lock lock = new ReentrantLock(); // 创建锁
1.2、使用锁创建一个 Condition 实例
Condition condition = lock.newCondition(); // 创建 Condition
2、源码
2.1、ReentrantLock 中的 newCondition 方法,调用 同步器中的 newCondition方法,创建一个 AQS 中的 ConditionObject 实例 返回
// ReentrantLock 下的 newCondition方法
public Condition newCondition() {
return sync.newCondition(); // 调用 同步器中的 newCondition方法
}
// ReentrantLock 中内部类 Sync 的newCondition 方法
final AbstractQueuedSynchronizer.ConditionObject newCondition() {
return new AbstractQueuedSynchronizer.ConditionObject(); // 创建一个 AQS 中的 ConditionObject 实例 返回
}
2.2、 AQS 中的 ConditionObject内部类
2.2.1、 await方法 - 将线程进入等待状态
步骤:
创建一个新的节点,将线程加入到条件队列(等待队列) --> 释放该线程获取的所有锁 --> 判断是否在同步队列中,不在同步队列中,则停止该线程 --> 若线程在同步队列中,则改变线程的状态,更新同步队列,然后停止该线程;
public final void await() throws InterruptedException {
if (Thread.interrupted()) // 若线程被中断,则抛出异常
throw new InterruptedException();
AbstractQueuedSynchronizer.Node node = addConditionWaiter(); // 创建一个新的节点,将线程加入到条件队列(等待队列)
int savedState = fullyRelease(node); // 释放该线程获取的所有锁
int interruptMode = 0;
while (!isOnSyncQueue(node)) { // 判断是否在同步队列中,不在同步队列中,则停止该线程
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 若线程在同步队列中,则停止该线程
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) // 改变线程的状态
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters(); // 更新同步队列
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
2.2.2、signal方法 - 唤醒线程
步骤:
取出该condition条件队列中的头结点 --> 递归唤醒该 condition 下所有的 await的线程
public final void signal() {
if (!isHeldExclusively()) // 是否为独占锁节点
throw new IllegalMonitorStateException();
AbstractQueuedSynchronizer.Node first = firstWaiter; // 取出该condition条件队列中的头结点
if (first != null)
doSignal(first);
}
private void doSignal(AbstractQueuedSynchronizer.Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) && // 唤醒该 condition 下所有的 await的线程
(first = firstWaiter) != null); // 递归唤醒所有
}
3、同步队列与条件队列(等待队列)
对于同一个“AQS”来说,同步队列只有一个,而条件队列有多个,每个Condition实例都有一个自己的条件队列
同步队列:正在排队等待获取锁的队列
条件队列(等待队列):已经获取到锁,但被 await 后的线程所进入的队列;待队列被唤醒的时候,则会将队列中的线程加入到同步队列中取竞争锁
三、CountDownLatch、CyclicBarrier、Semaphore源码解析
1、CountDownLatch 源码解析
-
使用共享锁实现
-
当其他全部线程未执行完毕调用 countDown 方法时,则主线程在调用await处处于等待状态
-
解析构造方法、countDown方法、await方法
// 1、创建 CountDownLatch 实例
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count); // 创建一个同步器实例
}
Sync(int count) {
setState(count); // 设置state初始化的状态值
}
// 2、在其他每个线程中调用一次 latch.countDown()方法 计数
// latch.countDown();
public void countDown() {
sync.releaseShared(1); // 调用AQS的方法 释放一个锁,当 state 为 0 时,latch.await()的线程才能被唤醒
}
// 3、在需要汇总操作前的位置 await 当前线程,当计数线程执行完销毁后,会自动唤醒当前线程
// latch.await();
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1); // 调用了AQS 的acquireSharedInterruptibly方法
}
// AQS
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted()) // 查看当前线程是否为已中断状态
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) // 调用了AQS的实现类CountDownLatch的 tryAcquireShared 方法
doAcquireSharedInterruptibly(arg); // 中断当前线程
}
// CountDownLatch
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1; // 若其他线程都执行完毕(调用了latch.countDown()),则返回 1,否则返回 -1,中断当前线程
}
2、CyclicBarrier源码解析
-
设置一个屏障点,使其他线程到达后调用 barrier.await(); 进行等待,直到指定条件达到时,由最后一个线程执行屏障点线程
-
解析构造方法、await方法
// 1、在主线程中创建一个 CyclicBarrier 的实例;条件达到时会唤醒被await 的所有线程并执行 参数线程动作
/*CyclicBarrier barrier = new CyclicBarrier(10, new Runnable() { // 参数为 (等待的线程数,达到等待数条件时执行的线程动作)
@Override
public void run() {
// 执行动作
}
});*/
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties; // 记录需要等待的线程数
this.barrierCommand = barrierAction; // 记录目的线程
}
// 2、在其他线程中调用 await
// barrier.await();
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L); // 调用 dowait 来 await 线程,或在达到条件下唤醒所有线程并执行屏障点线程
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
// 核心方法 dowait
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock; // 获取锁,为了线程安全
lock.lock();
try {
// 为提供reset方法设置,先不管 。。。
final CyclicBarrier.Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) { // 判断线程是否已被中断
breakBarrier();
throw new InterruptedException();
}
int index = --count; // 将所需达到条件线程数目 -1
if (index == 0) { // 若 index == 0,则表示已达到条件,可唤醒全部线程并执行屏障点线程
boolean ranAction = false;
try {
final Runnable command = barrierCommand; // 获取屏障点线程
if (command != null)
command.run(); // 执行 屏障点线程run方法
ranAction = true;
nextGeneration(); // 唤醒所有线程并重置generation
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
//当不满足 屏障点条件时,则让当前线程await
for (;;) {
try {
if (!timed)
trip.await(); // await线程
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
3、Semaphore源码解析
-
限定指定数目的线程调用方法,其他线程只能被阻塞在外等待,类似一个限定了线程数目大小的共享锁,acquire获取锁、release释放锁
-
使用了共享锁实现
-
解析构造方法、acquire方法、release方法
// 1、创建一个 Semaphore 的实例;参数为可进入的线程数目
// Semaphore semaphore = new Semaphore(10);
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
NonfairSync(int permits) {
super(permits); // 调用AQS的构造器方法,设置state 的初始状态值,也就可进入的线程数目
}
Sync(int permits) {
setState(permits);
}
// 2、调用 acquire 方法计数,每调用一次,state都会 -1
// semaphore.acquire();
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg); // 当上判断返回值 < 0时,则说明此时进入的线程已达限定值,让接下来进入的线程进入同步队列等待
}
// tryAcquireShared 方法的调用,将状态值 -1,当可进入线程数目 > 0时,则在此自旋;否则返回-1
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
// 3、调用 release方法计数,每调用一次,state都会 +1
public void release() {
sync.releaseShared(1);
}
// 若线程释放成功,则唤醒同步队列的线程
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared(); // 若释放成功,则唤醒同步队列中的一个线程
return true;
}
return false;
}
// 进行线程的释放,将状态值 +1
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
有啥不正确的还望大佬指正,谢谢