var chan = new SynchronousQueue<Integer>();var worker = new Thread(()->{while(true){try {final var x = chan.take();System.out.println("t1 consume: " + x);} catch (InterruptedException e) {e.printStackTrace();}}});var worker2 = new Thread(()->{while(true){try {final var x = chan.take();System.out.println("t2 consume: " + x);} catch (InterruptedException e) {e.printStackTrace();}}});worker.start();worker2.start();for(var i = 0 ; i < 10; i ++) chan.put(i);复制代码基于所有权的角度去分析,生产者 — 消费者模式和阻塞队列一起促进了 串行的线程封闭 。线程封闭对象只能由单个对象拥有,但可以通过在执行的最后发布该对象 ( 即表示之后不会再使用它 ),以表示 "转让" 所有权 。阻塞队列简化了转移的逻辑 。除此之外,还可以通过 ConcurrentMap 的原子方法 remove,或者是 AtomicReference 的 compareAndSet ( 即 CAS 机制 ) 实现安全的串行线程封闭 。
双端队列和工作窃取Java 6 之后增加了新的容器类型 —— Deque 和 BlockDeque,它们是对 Queue 以及 BlockingQueue 的拓展 。Deque 实现了再队列头和队列尾的高效插入和移除,具体实现包括了 ArrayDeque 和 LinkedBlockingDeque 。
双端队列适用于另一种工作模式 —— 工作窃取 ( Work Stealing ) 。比如,一个工作线程已经完成清空了自己的任务队列,它就可以从其它忙碌的工作线程的任务队列的尾部获取队列 。这种模式要比生产者 —— 消费者具备更高的可伸缩性,因为工作线程不会在单个共享的任务队列上发生竞争 。
工作窃取特别适合递归的并发问题,即执行一个任务时会产生更多的工作,比如:Web 爬虫,GC 垃圾回收时的图搜索算法 。
阻塞和中断方法线程可能会被阻塞,或者是暂停执行,原因有多种:等待 I/O 结束,等待获得锁,等待从 Thread.sleep 中唤醒,等待另一个线程的计算结果 。被阻塞的线程必须要在这些 "外因" 被解决之后才有机会继续执行,即恢复到 RUNNABLE ( 也称就绪 ) 状态,等待被再次调度 CPU 执行 。
这段描述其实对应了 JVM 线程的两个状态:BLOCKING 和 WAITING 。
- BLOCKING,当线程准备进入一段新的同步代码块时,因不能获得锁而等待 。
- WAITING,当线程已经进入同步代码块之后,在执行的过程中因不满足某些条件而暂停 。这时可以调用 waiting 方法 释放已占据的锁 。其它工作线程得以抢占此锁并执行,直到满足先验条件为真时,其它线程可以通过 notifyAll 方法重新令监视此锁的所有 WAITING 线程再次争锁并继续工作 。 wait / notify / notifyAll 构成了线程之间的协商机制,见下面的代码块 。
static class Status{public boolean v;}public static void main(String[] args) throws InterruptedException{var status = new Status();status.v = false;var mutex = new Object();new Thread(()->{synchronized (mutex){System.out.println("get mutex");// 此时检测的状态为 false, 进入 WAITING 状态 。if(!status.v) try {mutex.wait();} catch (InterruptedException e) {e.printStackTrace();}// 被唤醒后重新检测状态为 true 。System.out.println(status.v);}}).start();new Thread(()->{synchronized (mutex){// 将状态设置为 true,唤醒上面的线程status.v = true;mutex.notify();}}).start();}复制代码只有处于 RUNNABLE 状态的线程才会实际获得 CPU 使用权 。Java中哪些操作会使线程释放锁资源_后端码匠的博客-CSDN博客_线程释放锁资源
JVM中的线程状态 - 知乎 (zhihu.com)
在 Java 中,一切会发生阻塞的方法都会被要求处理 InterruptedException 受检异常 。调用阻塞方法的方法也会变成阻塞方法 。线程内部有一个 boolean 类型的状态位表示中断,调用 interrupt 方法可以将该状态位标识为 true 。但是这不意味着该线程就会立刻中断:
推荐阅读
- 使用 JavaScript 实现无限滚动
- 如何自己开发漏洞扫描工具
- Firefox浏览器|火狐浏览器新增实用工具:将支持图片文字识别
- Java|Java:2022年招聘Java开发人员指南
- Java|HR傲慢对待求职者,还“诅咒”对方找不到工作,大学生也太难了
- 什么资源都能搜?这款搜索引擎工具,一键检索各大网盘资源
- 在Java 8及更高版本中使用Java流
- Java实现第三方短信接口发送短信验证码
- 3个超强资源搜索工具 资源搜索工具
- 连裤袜|“颜值经济”盛行,美妆工具受追捧,我国美妆工具市场需求增势明显
