Condition
1、Condition概述
对于synchronized,可以结合wait/notify实现线程的通信,Condition则是JUC中提供的一个多线程协调通信的工具类,可以让某些线程等待某个条件(condition),只有满足条件时才会被唤醒。
condition.await( )和condition.signal( )使用示例:和wait/notify一样,await/signal也要在lock( )和unlock( )之间使用。
public class ConditionDemoWait implements Runnable{ private Lock lock; private Condition condition; public ConditionDemoWait(Lock lock, Condition condition){ this.lock=lock; this.condition=condition; } @Override public void run() { System.out.println("begin ConditionDemoWait"); try { lock.lock(); condition.await(); System.out.println("end - ConditionDemoWait"); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } }
public class ConditionDemoSignal implements Runnable{ private Lock lock; private Condition condition; public ConditionDemoSignal(Lock lock, Condition condition){ this.lock=lock; this.condition=condition; } @Override public void run() { System.out.println("begin ConditionDemoSignal"); try { lock.lock(); condition.signal(); System.out.println("end - ConditionDemoSignal"); }finally { lock.unlock(); } } }
2、Condition原理分析
调用Condition,需要获得Lock锁,所以意味着会存在一 个AQS同步队列,而Condition也是通过AbstractQueuedSynchronizer内部的ConditionObject具体实现的,其内部维护一个单向链表(也成为Condition队列)。
假设有线程A和线程B状态如下:
(1)当在线程A中调用condition.await( ),将ThreadA封装成Node节点(waitStatus=CONDITION),将Node添加到Condition队列尾部,然后释放锁(此时AQS中state=0),通过LockSupport.park( )阻塞在调用处。此时AQS队列挂起的线程被唤醒竞争锁。
(2)当在线程B中调用condition.signal( )(注意要和A中调用的condition为同一个对象),将Condition队列的头节点通过CAS添加到AQS队列的尾部,firstWaiter指向原头节点的后继节点,此时原头节点有资格去竞争锁。
CountDownLatch
1、CountDownLatch(共享锁)
countdownlatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程做操作执行完。提供了两个方法,一个是await( ),一个是countDown( ),countdownlatch初始化时需要传入一个整数,在这个整数倒数到0之前,调用了await方法的线程都要等待,然后通过countDwon方法来倒数。
使用示例:
public class Demo extends Thread{
static CountDownLatch countDownLatch=new CountDownLatch(1);
@Override public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<1000;i++){
new Demo().start();
}
countDownLatch.countDown();
}
}
从示例可以看出,countDownLatch可以做压测。每次调用countDown( ),state会减1,当state减到0时之前调用await阻塞的线程都被唤醒执行。
2、CountDownLatch原理
countDownLatch也使用到了AQS队列,当一个线程内调用await( )时,会将该节点封装成Node(waitStatus=PROPAGATE,共享模式下才节点才会处于这个状态,处于这个状态下的节点,会对线程的唤醒进行传播)添加到AQS队列中,并为阻塞状态。当倒数到state=0时,会从头节点依次唤醒AQS队列中的所有节点。
其他工具类:
1、Semaphore,可以控制同时访问的线程个数,常用于限流功能。
使用示例:
public class Test { public static void main(String[] args) { Semaphore semaphore=new Semaphore(5); for(int i=0;i<10;i++){ new Car(i,semaphore).start(); } } static class Car extends Thread{ private int num; private Semaphore semaphore; public Car(int num, Semaphore semaphore) { this.num = num; this.semaphore = semaphore; } public void run(){ try { semaphore.acquire();// 获取一个许可 System.out.println("第"+num+"占用 一个停车位"); TimeUnit.SECONDS.sleep(2); System.out.println("第"+num+"俩车 走喽"); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
2、CyclicBarrier,可循环使用的屏障,让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会打开,所有被屏障拦截的线程才会继续工作。