阻塞队列理论
ArrayBlockingQueue :是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出) 原则对元素进行排序
LinkedBlockingQueue: 一个基于链表结构的阻塞队列,此队列按照FIFO(先进先出)排序元素,吞吐量通常高于ArrayBlockingQueue
synchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量要高于ArrayBlockingQueue
阻塞队列
当阻塞队列是空时,从队列中获取元素的操作将会阻塞
当阻塞队列是满时,往队列里添加元素的操作将会阻塞
试图从空的阻塞队列中获取元素将会被阻塞,直到其他线程往空的队列中插入新的元素
试图往已满的阻塞队列中添加新元素将会被阻塞,直到其他线程从队列中移除一个或多个元素或者清空队列使队列重新变的空闲起来并后续新增
阻塞队列 好处
在多线程领域:所谓阻塞,在某些情况下会挂起线程,一旦条件满足,被挂起的线程又会自动被唤醒
好处是:我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办
在concurrent发布之前,在多线程环境下,我们需要自己去控制这些细节,尤其还要兼顾线程安全和效率,增加复杂度
阻塞队列种类分析
**ArrayBlockingQueue: 由数组构成的有界阻塞队列**
**LinkedBlockingQueue:由链表结构组成的有界(默认值为Integer.MAX_VALUE)**
PriorityBlockingQueue: 支持优先级排序的无界阻塞队列
DelayQueue: 使用优先级队列实现的延迟无界阻塞队列
**SynchronousQueue: 不存储元素的阻塞队列,也即单个元素的队列**
LinkedTransferQueue: 由链表结构组成的无界阻塞队列
LinkedBlockingDeque:由链表结构组成的双向阻塞队列
BlockingQueue
抛异常
public static void main(String[] args) {
BlockingQueue<String> deque = new ArrayBlockingQueue<String>(3);
deque.add("a");
deque.add("b");
deque.add("c");
//deque.add("d"); //Exception in thread "main" java.lang.IllegalStateException: Queue full
deque.remove("a"); //移除 A 元素
System.out.println(deque.element()); // a 已经 被 移除 所以输出b
deque.remove();
deque.remove();
deque.remove();//Exception in thread "main" java.util.NoSuchElementException
deque.remove();
}
当阻塞队列满时,再往队列里add插入元素会抛出异常
当阻塞队列空时,再往队列里remove移除元素抛出异常
特殊值
public static void main(String[] args) {
BlockingQueue<String> deque = new ArrayBlockingQueue<String>(3);
System.out.println(deque.offer("a")); //ture
System.out.println(deque.offer("b")); //ture
System.out.println(deque.offer("c")); //ture
System.out.println(deque.offer("d")); //false
System.out.println(deque.peek()); // a
System.out.println(deque.poll()); // a
System.out.println(deque.poll()); // b
System.out.println(deque.poll()); // c
System.out.println(deque.poll()); // null 没有返回 null
}
插入方法,成功TRUE 失败 FALSE
移除方法,成功返回队列中的元素 ,没有返回null
一直阻塞
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> deque = new ArrayBlockingQueue<String>(3);
deque.put("a");
deque.put("b");
deque.put("c");
deque.put("d"); // 队列满了 阻塞等待
deque.take();
deque.take();
deque.take();
deque.take(); // 队列空了 阻塞
}
当阻塞队列满时,生产者线程继续往队列里PUT元素,队列会一直阻塞生产线程直到PUT数据or响应中断退出
当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用
超时退出
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> deque = new ArrayBlockingQueue<String>(3);
System.out.println(deque.offer("a", 2L, TimeUnit.SECONDS));
System.out.println(deque.offer("b", 2L, TimeUnit.SECONDS));
System.out.println(deque.offer("c", 2L, TimeUnit.SECONDS));
System.out.println(deque.offer("d", 2L, TimeUnit.SECONDS));//超时 将返回FALSE
System.out.println(deque.poll(2L, TimeUnit.SECONDS));
System.out.println(deque.poll(2L, TimeUnit.SECONDS));
System.out.println(deque.poll(2L, TimeUnit.SECONDS));
System.out.println(deque.poll(2L, TimeUnit.SECONDS)); //没有 将返回FALSE
}
当阻塞队列满时,队列会阻塞生产者线程一定时间,超时后限时候生产者线程会退出
SynchronousQueue
SynchronousQueue没有容量
与其他BlockingQUeue不同,SynchronousQueue是一个不存储元素的BlockingQUeue
每一个put操作必须等待一个take操作,否则不能继续添加元素,反之亦然
public static void main(String[] args) {
BlockingQueue queue = new SynchronousQueue();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " \t " + "1");
queue.put("1");
System.out.println(Thread.currentThread().getName() + " \t " + "2");
queue.put("2");
System.out.println(Thread.currentThread().getName() + " \t " + "3");
queue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + " \t " + "1");
queue.take();
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + " \t " + "2");
queue.take();
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + " \t " + "3");
queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
生产者消费者
lock
多个条件
/**
* A 打印 5 次
* B 打印 10 次
* C 打印 15 次
* 接着又是 A
*/
public class TestManyCondition {
public static void main(String[] args) {
Resouce resouce = new Resouce();
new Thread(() -> {
try {
resouce.print5();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
resouce.print10();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
resouce.print15();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
class Resouce {
ReentrantLock lock = new ReentrantLock();
private volatile int num = 1;
Condition a = lock.newCondition();
Condition b = lock.newCondition();
Condition c = lock.newCondition();
public void print5() throws InterruptedException {
lock.lock();
try {
while (num != 1) {
a.await();
}
for (int i = 0; i < 5; i++) {
System.out.println("A");
}
num = 2;
b.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10() throws InterruptedException {
lock.lock();
try {
while (num != 2) {
b.await();
}
for (int i = 0; i < 10; i++) {
System.out.println("B");
}
num = 3;
c.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15() throws InterruptedException {
lock.lock();
try {
while (num != 3) {
c.await();
}
for (int i = 0; i < 15; i++) {
System.out.println("C");
}
num = 1;
a.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
虚假唤醒
public class TestProdConsumer_lock {
public static void main(String[] args) {
Mydata2 mydata = new Mydata2();
new Thread(()->{
for (int i = 0; i < 5; i++) {
mydata.incr();
}
},"AA").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
mydata.decr();
}
},"BB").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
mydata.incr();
}
},"CC").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
mydata.decr();
}
},"DD").start();
}
}
class Mydata2{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private volatile int num = 0;
public void incr(){
lock.lock();
try{
if (num != 0 ){ // 等待
condition.await();
}
//干活
num++;
System.out.println(Thread.currentThread().getName() + " " + num);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decr(){
lock.lock();
try{
if (num == 0 ){ // 等待
condition.await();
}
//干活
num--;
System.out.println(Thread.currentThread().getName() + " " + num);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
AA 1
BB 0
CC 1
AA 2
DD 1
DD 0
BB -1
BB -2
BB -3
BB -4
CC -3
AA -2
DD -3
DD -4
DD -5
CC -4
AA -3
CC -2
AA -1
CC 0
Process finished with exit code 0
*/
解决
将if换成while
public class TestProdConsumer_lock {
public static void main(String[] args) {
Mydata2 mydata = new Mydata2();
new Thread(()->{
for (int i = 0; i < 5; i++) {
mydata.incr();
}
},"AA").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
mydata.decr();
}
},"BB").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
mydata.incr();
}
},"CC").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
mydata.decr();
}
},"DD").start();
}
}
class Mydata2{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private volatile int num = 0;
public void incr(){
lock.lock();
try{
while (num != 0 ){ // 等待
condition.await();
}
//干活
num++;
System.out.println(Thread.currentThread().getName() + " " + num);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decr(){
lock.lock();
try{
while (num == 0 ){ // 等待
condition.await();
}
//干活
num--;
System.out.println(Thread.currentThread().getName() + " " + num);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
队列版本
public class TestProdConsumer_Queue {
public static void main(String[] args) throws InterruptedException {
MySources sources = new MySources(new ArrayBlockingQueue<String>(10));
new Thread(()->{
try {
sources.prod();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"p").start();
new Thread(()->{
try {
sources.consu();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
TimeUnit.SECONDS.sleep(5);
sources.stop();
}
}
class MySources {
private BlockingQueue<String> blockingQueue = null;
private volatile Boolean flag = true;
private AtomicInteger integer = new AtomicInteger(0);
public MySources(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
}
public void prod() throws InterruptedException {
String data = "";
while (flag) {
TimeUnit.SECONDS.sleep(1);
data = integer.incrementAndGet() + "";
boolean offer = blockingQueue.offer(data, 2, TimeUnit.SECONDS);
if (offer) {
System.out.println(Thread.currentThread().getName() + "\t 放入成功" + " \t " + data);
} else {
System.out.println(Thread.currentThread().getName() + "\t 放入失败");
}
}
System.out.println("停止生产");
}
public void consu() throws InterruptedException {
while (flag) {
TimeUnit.SECONDS.sleep(1);
String result = blockingQueue.poll(2, TimeUnit.SECONDS);
if (result == null || result.equalsIgnoreCase("")) {
System.out.println(Thread.currentThread().getName() + "\t 获取失败");
} else {
System.out.println(Thread.currentThread().getName() + "\t 获取成功" + " \t " + result);
}
}
System.out.println("不让消费");
}
public void stop(){
flag = false;
}
}
Synchronized 和 lock区别
1、原始构成
synchronized是关键字 属于JVM层面
monitorenter(底层通过monitor对象来完,其实wait/notify等方法也依赖与monitor对象只有在同步块或者方法中才能调用wait/notify)
monitorexit (两个 防止异常死锁)
Lock是具体类,是api层面的锁
2、使用方法
synchronized 不需要用户手动释放锁,当synchronized代码执行完成系统会自动让线程释放锁的占用
ReentranLock 则需要用户手动去释放锁,若没有手动释放锁,则可能导致死锁
需要lock() 和 unlock() 方法配合try/finally语句完成
3、等待是否可中断
synchronized 不可中断,除非抛出异常或者正常运行完成
ReentranLock 可中断,1.设置超时方法,tryLock(long,timeout,TimeUnit unit)
2.lockInterruptibly() 放在代码块中,调用interrupt()方法可以中断
4、加锁是否公平
synchronized 非公平锁
ReentranLock 都可以 默认非公平 构造方法可以传入 bool值 TRUE 为公平 FALSE 为非公平锁
5、锁绑定多个条件Condition
synchronized 没有
ReentranLock 用来实现分组唤醒需要唤醒的线程,可以精确唤醒,而不是像synchronized 随机唤醒一个线程要么唤醒全部线程