java.util.concurrent包系列文章
JUC—ThreadLocal源码解析(JDK13)
JUC—ThreadPoolExecutor线程池源码解析(JDK13)
JUC—各种锁(JDK13)
JUC—原子类Atomic*.java源码解析(JDK13)
JUC—CAS源码解析(JDK13)
JUC—ConcurrentHashMap源码解析(JDK13)
JUC—CopyOnWriteArrayList源码解析(JDK13)
JUC—并发队列源码解析(JDK13)
JUC—多线程下控制并发流程(JDK13)
JUC—AbstractQueuedSynchronizer解析(JDK13)
控制并发流程的工具类概览
倒数计数器,倒数结束之前,一直处在等待状态,直到计时结束,此线程才能继续工作
唯一的构造方法
// 传入倒数计数数量
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
await方法。调用await的线程会被挂起,直到count的数量为0才会继续执行
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
倒数的方法countDown,调用1次count减1。直到count为0,等待的线程会被唤醒。
public void countDown() {
sync.releaseShared(1);
}
使用示例
1、一等多:一个线程等待其他线程完成工作之后继续执行,这里演示主线程等待子线程执行完毕再继续执行。
譬如拼团网购,需要5个人一起拼,只有凑齐5个之后才能拼团成功。
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("用户No." + Thread.currentThread().getId() + "加入了拼团");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}
};
service.submit(runnable);
}
System.out.println("等待拼团用户达到5个");
latch.await();
System.out.println("凑满5个人拼团啦,可以生成订单了");
}
执行结果:
等待拼团用户达到5个
用户No.14加入了拼团
用户No.18加入了拼团
用户No.15加入了拼团
用户No.16加入了拼团
用户No.17加入了拼团
凑满5个人拼团啦,可以生成订单了
2、多等一:多个线程等待某一个线程完成工作之后继续执行。
譬如开车的时候,路上等红灯。有4个车道,大家都在等待绿灯亮起,大家一起出发。
public static void main(String[] args) throws InterruptedException {
CountDownLatch begin = new CountDownLatch(1);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("No." + no + "在等红灯,等待绿灯亮起");
try {
begin.await();
System.out.println("No." + no + "开始踩油门加速了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.submit(runnable);
}
Thread.sleep(5000);
System.out.println("红灯亮起,大家可以开车了!");
begin.countDown();
}
执行结果:
No.2在等红灯,等待绿灯亮起
No.5在等红灯,等待绿灯亮起
No.4在等红灯,等待绿灯亮起
No.3在等红灯,等待绿灯亮起
No.1在等红灯,等待绿灯亮起
红灯亮起,大家可以开车了!
No.2开始踩油门加速了
No.1开始踩油门加速了
No.3开始踩油门加速了
No.4开始踩油门加速了
No.5开始踩油门加速了
注意:不能重用,想重用可以新建一个CountDownLatch实例或者使用CyclicBarrier
信号量,限制有限资源的使用量。相当于许可证。数量有限的。拿到许可证就可以执行。
譬如耗时的服务和资源,限制同一时刻并发请求的数量。
假如信号量设置为3,线程1-2-3获得了信号量许可在请求服务,线程4没有获得信号量,就阻塞等待,线程1-2-3执行请求完毕,执行release()方法释放一个信号量之后,线程4就可以或者执行请求服务的机会。
构造方法
// 可以选择公平和非公平策略,公平策略会优先考虑等待时间最长的线程,新加入的线程排在队尾
// 非公平策略允许新来的线程在一定条件下插队
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
其他方法
使用示例
static Semaphore semaphore = new Semaphore(5, true);
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(50);
for (int i = 0; i < 5; i++) {
service.submit(new Task());
}
service.shutdown();
}
static class Task implements Runnable {
@Override
public void run() {
try {
semaphore.acquire(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了许可证");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "释放了许可证");
// 可以选择释放指定数量的信号量,不过获取和释放的数量最好一致
semaphore.release(3);
}
}
执行结果:
pool-1-thread-2拿到了许可证
pool-1-thread-2释放了许可证
pool-1-thread-1拿到了许可证
pool-1-thread-1释放了许可证
pool-1-thread-5拿到了许可证
pool-1-thread-5释放了许可证
pool-1-thread-4拿到了许可证
pool-1-thread-4释放了许可证
pool-1-thread-3拿到了许可证
pool-1-thread-3释放了许可证
当线程1要等待某个条件的时候就去执行condition.await(),就进入阻塞状态,等待被唤醒。
这个时候线程2去执行了这个条件,调用condition.single()或者singleAll()方法,JVM就会从被阻塞的线程里找到那些等待这个condition的线程,线程1收到可执行信号的时候,它的线程状态就会变成可执行状态。
single()唤醒等待时间最长的那个线程,singleAll()唤醒所有等待线程。
public class ConditionDemo {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
private Lock lock = new ReentrantLock();
// 没有满的Condition
private Condition notFull = lock.newCondition();
// 没有空的Condition
private Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
ConditionDemo2 conditionDemo2 = new ConditionDemo2();
Producer producer = conditionDemo2.new Producer();
Consumer consumer = conditionDemo2.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread {
@Override
public void run() {
consume();
}
private void consume() {
while (true) {
lock.lock();
try {
while (queue.size() == 0) {
System.out.println("队列空,等待数据");
try {
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll();
notFull.signalAll();
System.out.println("从队列里取走了一个数据,队列剩余" + queue.size() + "个元素");
} finally {
lock.unlock();
}
}
}
}
class Producer extends Thread {
@Override
public void run() {
produce();
}
private void produce() {
while (true) {
lock.lock();
try {
while (queue.size() == queueSize) {
System.out.println("队列满,等待有空余");
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(1);
notEmpty.signalAll();
System.out.println("向队列插入了一个元素,队列剩余空间" + (queueSize - queue.size()));
} finally {
lock.unlock();
}
}
}
}
}
Condition是Object.wait()的升级版。如果说Lock用来代替synchronized的,Condition就是用来代替Object.wait()和Object.notify()的。
调用await之前必须手动持有锁,否则抛异常。调用await方法会自动释放锁,与Object.wait()一样。
CyclicBarrier循环栅栏,类似于CountDownLatch。
它们的不同点
使用示例
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("所有人都到场了, 大家统一出发!");
}
});
for (int i = 0; i < 6; i++) {
new Thread(new Task(i, cyclicBarrier)).start();
}
}
static class Task implements Runnable{
private int id;
private CyclicBarrier cyclicBarrier;
public Task(int id, CyclicBarrier cyclicBarrier) {
this.id = id;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("线程" + id + "现在前往集合地点");
try {
Thread.sleep((long) (Math.random()*10000));
System.out.println("线程"+id+"到了集合地点,开始等待其他人到达");
cyclicBarrier.await();
System.out.println("线程"+id+"出发了");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
执行结果:
线程3现在前往集合地点
线程1现在前往集合地点
线程5现在前往集合地点
线程0现在前往集合地点
线程2现在前往集合地点
线程4现在前往集合地点
线程0到了集合地点,开始等待其他人到达
线程4到了集合地点,开始等待其他人到达
所有人都到场了, 大家统一出发!
线程4出发了
线程0出发了
线程5到了集合地点,开始等待其他人到达
线程3到了集合地点,开始等待其他人到达
所有人都到场了, 大家统一出发!
线程3出发了
线程5出发了
线程2到了集合地点,开始等待其他人到达
线程1到了集合地点,开始等待其他人到达
所有人都到场了, 大家统一出发!
线程1出发了
线程2出发了