1. Condition接口
具体的方法实现在AbstractQueuedSychronizer内部类ConditionObject中,
condition等待队列:
单向链表,先进先出(FIFO),至少有一个节点,首节点不含元素,ConditionObject内部有firstWaiter与lastWaiter,单个元素就是AbstractQueuedSychronizer内部类Node,condition等待队列主要是使用nextWaiter属性;
await()方法:
(1).将调用线程加入condition等待队列中;
(2). 完全释放锁并唤醒竞争此锁的挂起线程(位于AQS等待队列中);
(3). 若此线程不在等待获取锁的AQS队列中,将此线程挂起LockSupport.park(this);
signal()方法:
(1). doSignal方法,condition等待队列为空,设置尾节点null,线程节点已被移出condition队列,设置首节点下一节点null;
(2). 将condition等待队列的尾结点加入到AQS等待队列,返回原来的condition等待队列尾节点; (3). 原尾结点的等待状态为取消或修改等待状态为SIGNAL可唤醒状态失败,直接唤起此节点线程LockSupport.unpark(node.thread);
package com.test.wei.javautilconcurrent.condition;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* condition 与 lock测试
*
* @author zhangshiwei
* @since 2020年8月28日 下午5:20:10
*/
public class ConditionTest {
public static void main(String[] args) {
Queue<String> msg = new LinkedList<>();
int maxSize = 5;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
ConditionProducer producer1 = new ConditionProducer(msg, maxSize, lock, condition);
ConditionProducer producer2 = new ConditionProducer(msg, maxSize, lock, condition);
ConditionConsumer consumer1 = new ConditionConsumer(msg, lock, condition);
ConditionConsumer consumer2 = new ConditionConsumer(msg, lock, condition);
Thread threadA = new Thread(producer1, "生产者A");
Thread threadB = new Thread(producer2, "生产者B");
Thread threadC = new Thread(consumer1, "消费者CCC");
Thread threadD = new Thread(consumer2, "消费者DDD");
threadA.start();
threadB.start();
threadC.start();
threadD.start();
boolean a = false;
}
/**
* condition 消费者
*
* @author zhangshiwei
* @since 2020年8月28日 下午5:17:08
*/
public static class ConditionConsumer implements Runnable {
private Queue<String> msg;
private Lock lock;
private Condition condition;
public ConditionConsumer(Queue<String> msg, Lock lock, Condition condition) {
this.msg = msg;
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
while (true) {
lock.lock();
while (msg.isEmpty()) {
System.out.println("队列已空 ------ 消费者 " + Thread.currentThread().getName() + " 等待队列有产品");
try {
// 阻塞线程并释放锁 - 让生产者拿到锁去生产
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - 消费: " + msg.remove());
// 唤醒处于阻塞状态的线程 - 即唤醒阻塞的生产者 - 让生产者开始生产
condition.signal();
lock.unlock();
}
}
}
/**
* condition 生产者
*
* @author zhangshiwei
* @since 2020年8月28日 下午5:07:13
*/
public static class ConditionProducer implements Runnable {
private Queue<String> msg;
private Integer maxSize;
private Lock lock;
private Condition condition;
public ConditionProducer(Queue<String> msg, Integer maxSize, Lock lock, Condition condition) {
this.msg = msg;
this.maxSize = maxSize;
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
int i = 0;
while (true) {
i++;
lock.lock();
while (msg.size() == maxSize) {
System.out.println("队列已满 ------ 生产者 " + Thread.currentThread().getName() + " 先等待队列产品被消费掉");
try {
// 阻塞线程并释放锁 - 让消费者拿到锁去消费
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - 生产: " + i);
msg.add("仙丹" + i);
// 唤醒处于阻塞状态的线程 - 即唤醒阻塞的消费者 - 让消费者开始消费
condition.signal();
lock.unlock();
}
}
}
}
执行结果:
2. 阻塞队列BlockingQueue
BlockingQueue内部:
ReentrantLock lock = new ReentrantLock();
Condition notEmpty = condition.newCondition(); 用于取出元素出队;
Condition notFull = condition.newCondition(); 用户加入元素入队;
可有两个元素操作此队列;
put(E e)方法向阻塞队列加入元素: 若队列满了就一直阻塞等待,直到有空间可加入元素;
take()方法取出元素: 若队列空了,就一直阻塞等待,直到队列中有元素返回元素;
package com.test.wei.javautilconcurrent.blockingqueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
/**
* LinkBlockingQueue 测试
*
* @author zhangshiwei
* @since 2020年8月29日 上午12:52:38
*/
public class LinkBlockingQueueTest {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new LinkedBlockingDeque<>(5);
for (int i = 1; i < 10; i++) {
new Thread(new BlockingQueueProducer(blockingQueue, i), "线程" + i).start();
new Thread(new BlockingQueueConsumer(blockingQueue), "线程" + i).start();
}
}
// take : 阻塞式的取出元素, 队列空了,一直阻塞,直到有元素
// remove : 队列空了抛异常
// poll : 队列空了返回null
public static class BlockingQueueProducer implements Runnable {
private BlockingQueue<String> msg;
private Integer num;
public BlockingQueueProducer(BlockingQueue<String> msg, Integer num) {
this.msg = msg;
this.num = num;
}
@Override
public void run() {
while (true) {
try {
String element = "元素" + num++;
Thread.sleep(2000);
msg.put(element);
System.out.println(Thread.currentThread().getName() + " - 放入: " + element);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// add : 队列满了 - 报异常
// offer : 队列满了, 返回false
// put : 阻塞式的添加元素, 队列满了,一直阻塞,直到有空间
public static class BlockingQueueConsumer implements Runnable {
private BlockingQueue<String> msg;
public BlockingQueueConsumer(BlockingQueue<String> msg) {
this.msg = msg;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " - 取出 " + msg.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
执行结果:
3. CountDownLatch倒计时闩锁
内部: private static final class Sync extends AbstractQueuedSynchronizer{ ...... }
这是一个计数器,线程完成一次调用一次countDown()方法,计数器就减一,当计数器state为0时就唤醒所有被挂起阻塞在Condition队列的线程, 之前调用了await()方法被阻塞的线程就能再次开始运行,
可用场景: 让某个线程等待其他线程等待一组线程全部执行完毕再执行;
await()方法: 创建node节点将此线程加入AQS队列中,挂起此线程;
countDown()方法: 计数器state减一,直到0,唤醒头节点,通过传递唤醒机制,最终唤醒AQS中的所有等待挂起的阻塞线程;
package com.test.wei.javautilconcurrent.countdownlatch;
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch 测试
*
* @author zhangshiwei
* @since 2020年8月31日 上午11:12:45
*/
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
countDownLatch.countDown();
System.out.println("线程: " + Thread.currentThread().getName() + " 执行结束" + countDownLatch.getCount());
}, "t" + i).start();
}
try {
System.out.println("线程: " + Thread.currentThread().getName() + " 调用 countDownLatch.await() , 主线程被阻塞");
countDownLatch.await();
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程: " + Thread.currentThread().getName() + " 执行结束");
}
}
执行结果:
4. Semaphore 信号灯,令牌
内部: abstract static class Sync extends AbstractQueuedSynchronizer { ...... }
acquire(): 获取令牌,修改state减一,获取失败就加入AQS队列阻塞挂起等待;
release(): 释放令牌,修改state加一;
package com.test.wei.javautilconcurrent.semaphore;
import java.util.concurrent.Semaphore;
/**
* 信号灯 令牌机制 测试
*
* @author zhangshiwei
* @since 2020年8月31日 下午4:20:25
*/
public class SemaphoreTest {
public static void main(String[] args) {
// 3个令牌
Semaphore semaphore = new Semaphore(3);
System.out.println("令牌semaphore : " + semaphore);
for (int i = 0; i < 20; i++) {
new Thread(() -> {
try {
// 获取到令牌就继续运行, 获取失败就阻塞AQS队列中
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 获取令牌 ");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 释放 令牌 ");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程" + i).start();
}
}
}
执行结果:
5. CyclicBarrier栅栏
内部:
private final ReentrantLock lock = new ReentrantLock();
private final Condition trip = lock.newCondition(); ......
await(): count减一,如果已经为0,则唤醒所有Condition队列中挂起的线程;
使用场景: 一组线程互相等待全部阻塞,知道满足某个条件再同时唤醒, 其中有reset()方法可以重用;
package com.test.wei.javautilconcurrent.cyclicbarrier;
import java.util.concurrent.CyclicBarrier;
/**
* CyclicBarrier
*
* @author zhangshiwei
* @since 2020年9月2日 下午2:09:04
*/
public class CyclicBarrierTest {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 进入线程,线程阻塞中...");
try {
// barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
cyclicBarrier.await();
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程阻塞结束,继续执行...");
}, i + "号").start();
Thread.sleep(1000);
}
try {
Thread.sleep(8000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("指令通知完成");
}
}
执行结果: