在上一次中已经实现一个生产者与消费者的初步模型(http://www.cnblogs.com/webor2006/p/8413286.html),但是当时只是一个生产者对应一个消费者,先贴下代码:

public classProductConsumerVersion2 {private final Object LOCK = newObject();private int i = 1;/*此标识用来说明是否当前已经生产过了,默认没有*/

private volatile boolean isProduced = false;private voidproduct() {synchronized(LOCK) {if(isProduced) {try{

LOCK.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}else{

i++;

System.out.println("P->" +i);

LOCK.notify();

isProduced= true;

}

}

}private voidcomsume() {synchronized(LOCK) {if(isProduced) {

System.out.println("C->" +i);

LOCK.notify();

isProduced= false;

}else{try{

LOCK.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

}public static voidmain(String[] args) {

ProductConsumerVersion2 productConsumerVersion2= newProductConsumerVersion2();new Thread("P") {

@Override

public void run() {

while (true)

productConsumerVersion2.product();

}

}.start();

new Thread("C") {

@Override

public void run() {

while (true)

productConsumerVersion2.comsume();

}

}.start();

}

}

而实际生产者消费者模型应该是多对多的,所以接下来将其改造一下:

首先将生产者改成多个,这里采用Java8中的特性来改造,如下:

接着类似的,将消费者也改造下:

等于目前已经有了两个生产者与两个消费者,那接下来运行看效果:

呃~~程序被阻塞住了,那是不是死锁了呢?可以用命令工具来查看确认一下【关于如何查看死锁可以参考:http://www.cnblogs.com/webor2006/p/8320749.html】:

先用"jps"命令查看当前程序的进程号,如下:

然后用"jstack"命令查看该进程的线程情况,如下:

那是为啥会卡住呢?下面增加一些调试语句用来定位跟踪,如下:

再次编译运行:

嗯~~整个过程可以清晰的看到了,那接下来则根据输出一一进行详细分析:

输出:P1 生产了-->2

第一个生产者来了,当然就直接生产喽,如下:

【注意】:上面的是对第一个红色的输出的解释,而不是第二个红色的输出哈~

输出:P1 notify了

输出:P1 wait了

接着第一个生产者又来了,由于之前生产的数据还木有被消费掉,那肯定就开始执行此分支了喽:

输出:P2 wait了

接着第二个生产者又来生产了,不用说继续wait()

输出:C1 消费了-->2

第一个消费者来了,开始消费之前生产的数据2,如下:

输出:C1 notify了

接着它去唤醒其它正在wait的线程,如下:

但是!!目前等待的线程有P1、P2,那唤醒的是哪一个线程呢?下面往下看

输出:C1 wait了

接着第一个消费者继续想消费,可此时没数据可消费了,则进入wait状态了,如下:

输出:P1 生产了-->3

对于上上个输出中提到了P1、P2都处于wait状态了,那C1这时notify()时唤醒的是哪个消费者呢?答案如输出,此时唤醒的是P1,所以它开始生产了:

注意:此时P2、C1还是处于wait状态。

输出:P1 notify了

接着它通知正处于wait状态的线程:

由于此时正在wait的线程有:P2、C1,那最终谁被唤醒了呢?继续分析

输出:P1 wait了

接着P1继续想生产,由于还没被消费掉,当然此时它又处于wait状态喽:

输出:P2 wait了

而上上个notify操作唤醒了这两个正在wait的P2、C1是哪个线程呢?输出也给了答案,很显然是P2,此时它想生产,但是此时还没被消费掉,那它继续wait:

输出:C2 消费了-->3

此时第二个消费者C2来了,由于已经有数据可消费了当然直接消费了喽:

输出:C2 notify了

消费完了,接着就是通知生产者继续生产,所以:

但是需要注意:此时wait的线程有C1、P2。那此时唤醒的是哪个线程呢?继续看一面。

输出:C2 wait了

接着C2继续想消费,但是很显然已经没有内容可消费了,于是乎开始wait:

输出:C1 wait了

而上上个notify发现最终唤醒的是C1线程,而由于目前没内容可消费了,那当然C1也wait了呗,所以:

最终C1、C2、P2都处理wait状态,P2线程wait了那P1也生产不了,而两个消费者线程也都wait了,进入死循环,于是乎就出现了假死的状态,这完全是程序逻辑所引起的,而非是真正的死锁,因为四个线程都wait等于是放弃了所有CPU的执行权,等着别人唤醒。

而造成问题的根本就是对于有多个wait的线程,一个notify具体唤醒的是哪个一线程,这个不同的JVM规则是不一样的,有些是按FIFO来唤醒的,有些是随机的,所以这个陷阱需要特别注意。

java启动子线程过多导致卡死_java线程基础巩固---多Product多Consumer之间的通讯导致出现程序假死的原因分析...相关推荐

  1. java ftp 假死_FTPClient下载文件程序假死问题

    FTPClient下载文件程序假死问题 FTPClient下载文件,程序假死问题 [所属类包] org.apache.commons.net.ftp.FTPClient [现象描述] 这两天java项 ...

  2. java 线程池 源码_java线程池源码分析

    我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...

  3. java不同进程的相互唤醒_Java线程生命周期与状态切换

    前提 最近有点懒散,没什么比较有深度的产出.刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期.状态切换以及线程的上下文切换等等.编写本文的时候, ...

  4. java 线程池 资源回收_JAVA线程池资源回收的问题

    最近项目中为了提高用户体验度,前台创建任务后台任务,用多线程来跑. 现在的场景:后台定时任务管理这两个线程池,一个最大线程数10个,一个最大线程数15.应用部署之后,不超过5个小时,服务器负载高,内存 ...

  5. java线程池的应用_Java线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  6. java线程池存在时间_Java线程池基础

    目录: 一.线程池概述 1.线程池类 目前线程池类一般有两个,一个来自于Spring,一个来自于JDK: 来自Spring的线程池:org.springframework.scheduling.con ...

  7. java 线程池的理解_JAVA线程池原理的理解

    线程池原理基础理解: 线程池初始化规定个数的线程,然后这些线程一直运行,并且监控线程队列,只要线程队列被添加进线程,那么线程池不断从队列中取出线程运行.直到队列中的线程为空.实例代码如下: packa ...

  8. java线程池 的方法_JAVA线程池的实现方法

    我们大家都知道,在处理多线程服务并发时,由于创建线程需要占用很多的系统资源,所以为了避免这些不必要的损耗,通常我们采用线程池来解决这些问题. 线程池的基本原理是,首先创建并保持一定数量的线程,当需要使 ...

  9. java线程池多线程优先级_Java线程优先级

    java线程池多线程优先级 Priority of a thread describes how early it gets execution and selected by the thread ...

最新文章

  1. 利用OpenCV实现旋转文本图像矫正的原理及OpenCV代码
  2. ubuntu下wget下载Linux内核源码、make生成.config文件
  3. 中国丝绸市场投资规划及未来发展前景展望报告2022-2028年
  4. MySQL批量更改数据库表结构字符集
  5. From 7.8 To 7.14
  6. 如何在ashx页面获取Session值(未将对象引用设置到对象的实例) (转)
  7. param参数服务器
  8. Atom飞行手册翻译: 4.4 Atom中的序列化
  9. 对警报线程池的警报线程_使用警报控制器的iOS操作表
  10. gvim下用Vundle安装solarized主题的方法
  11. 2019年2月22日 深入理解计算机系统(CS:APP)第一章读书笔记
  12. C - Catch That Cow POJ - 3278(广搜)
  13. CactiEZ-10.1版本安装和配置教程
  14. hive —— struct 复合结构
  15. springboot 集成 fastdfs
  16. Java获得指定时区时间
  17. 十九、顺序查找法和折半查找法
  18. kube-scheduler源码分析(四)之 findNodesThatFit
  19. 微软中国裁员计划曝光:年底前裁完!补偿不变!
  20. 每天做不完的分析报表,什么时候才能熬出头?

热门文章

  1. SCOM Logical Disk Free Space Monitor
  2. Mysql报错Fatal error:Can't open and lock privilege tables
  3. SVN的搭建及使用(三)用TortoiseSVN修改文件,添加文件,删除文件,以及如何解决冲突,重新设置用户名和密码等...
  4. DLL的编写与使用(C++)
  5. 程序员的十层楼   (1)
  6. 一台机器上启动多个tomcat
  7. linux shell awk BEGIN END 处理文本之前之后执行操作 简介
  8. linux shell 符号 变量 参数表示
  9. python 安装使用saltstack salt-api 简介
  10. linux 使用dd命令 写入镜像文件到u盘