java线程基础巩固---多Product多Consumer之间的通讯导致出现程序假死的原因分析
在上一次中已经实现一个生产者与消费者的初步模型(http://www.cnblogs.com/webor2006/p/8413286.html),但是当时只是一个生产者对应一个消费者,先贴下代码:
public class ProductConsumerVersion2 {
private final Object LOCK = new Object();
private int i = 1;
/* 此标识用来说明是否当前已经生产过了,默认没有 */
private volatile boolean isProduced = false; private void product() {
synchronized (LOCK) {
if (isProduced) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
i++;
System.out.println("P->" + i);
LOCK.notify();
isProduced = true;
}
}
} private void comsume() {
synchronized (LOCK) {
if (isProduced) {
System.out.println("C->" + i);
LOCK.notify();
isProduced = false;
} else {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args) {
ProductConsumerVersion2 productConsumerVersion2 = new ProductConsumerVersion2(); 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线程基础巩固---多Product多Consumer之间的通讯导致出现程序假死的原因分析的更多相关文章
- java线程基础巩固---多线程下的生产者消费者模型,以及详细介绍notifyAll方法
在上一次[http://www.cnblogs.com/webor2006/p/8419565.html]中演示了多Product多Consumer假死的情况,这次解决假死的情况来实现一个真正的多线程 ...
- Java 线程基础
Java 线程基础
- Java线程基础实例
概述 Java线程是一个在实战开发中经常使用的基础功能,而在Java中线程相关的类在java.lang和java.util.concurrent里 Thread package thread.base ...
- Java线程基础知识(状态、共享与协作)
1.基础概念 CPU核心数和线程数的关系 核心数:线程数=1:1 ;使用了超线程技术后---> 1:2 CPU时间片轮转机制 又称RR调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...
- java线程基础知识----线程与锁
我们上一章已经谈到java线程的基础知识,我们学习了Thread的基础知识,今天我们开始学习java线程和锁. 1. 首先我们应该了解一下Object类的一些性质以其方法,首先我们知道Object类的 ...
- java线程基础知识----线程基础知识
不知道从什么时候开始,学习知识变成了一个短期记忆的过程,总是容易忘记自己当初学懂的知识(fuck!),不知道是自己没有经常使用还是当初理解的不够深入.今天准备再对java的线程进行一下系统的学习,希望 ...
- java线程基础巩固---线程生命周期以及start方法源码剖析
上篇中介绍了如何启动一个线程,通过调用start()方法才能创建并使用新线程,并且这个start()是非阻塞的,调用之后立马就返回的,实际上它是线程生命周期环节中的一种,所以这里阐述一下线程的一个完整 ...
- Java 线程基础知识
前言 什么是线程?线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程 ID,当前指令指针 (PC),寄存器集合和堆栈组成.另外,线 ...
- java 线程基础篇,看这一篇就够了。
前言: Java三大基础框架:集合,线程,io基本是开发必用,面试必问的核心内容,今天我们讲讲线程. 想要把线程理解透彻,这需要具备很多方面的知识和经验,本篇主要是关于线程基础包括线程状态和常用方法. ...
随机推荐
- APT攻击与银行
APT与传统攻击 APT:团队作战,拥有自研工具集,意图明确,技术高超,拥有政府或商业背景,潜伏期多达数年. 传统攻击:脚本小子,水平参差不齐,短期攻击,直接获取利益为目标. 攻击目标 APT攻击链 ...
- Struts笔记5
文件下载 1.写action类 package com.gyf.web.action; import java.io.File; import java.io.FileInputStream; imp ...
- STM32F103芯片SPI控制NRF24L012.4G无线模块交互通信实验
1.NRF24L01模块的资料百度搜索一下就有很多.这里我就不做介绍本文主要侧重于应用层面实验介绍与分享. 2.先看下原理图. 根据原理图:写出NRF24L01 C语言驱动文件如下: #includ ...
- Pythn基础课程笔记day03_学习内容概要及作业讲解
第三天_学习内容概要 今日内容概要 1.整形 2.布尔类型 3.字符串 内容回顾和补充 内容回顾 利用思维导图,罗列复习自己学习的内容,巩固知识点. xmind 软件 processon 网站 补充 ...
- Scratch(二)来不及解释了,马上开始编程游戏
来来来,上一期你们都经过了”HelloWorld”神咒的加持,已入编程大门,我们今天就开始一边做游戏,一边熟悉Scratch. “我只是切出去抢了个红包,一回来就到了编程游戏的环节了?” 对,你没跑错 ...
- Vue、SPA实现登陆
axios/qs/vue-axios安装及使用步骤 首先我们要下载三个依赖包,方便后面的开发使用需要: npm install axios -S axios是vue2提倡使用的轻量版的ajax.它 ...
- 应用编排服务之ELK技术栈示例模板详解
日志对互联网应用的运维尤为重要,它可以帮助我们了解服务的运行状态.了解数据流量来源甚至可以帮助我们分析用户的行为等.当进行故障排查时,我们希望能够快速的进行日志查询和过滤,以便精准的定位并解决问题. ...
- Docker 学习笔记(四):问题日志
一.Docker-Compose 容器组开机重启失败 Docker-Compose 的 depends_on 参数在 docker 中没有对应. 重启电脑后,容器的重启过程只由 docker 控制,而 ...
- hdu 6625 three array (01-trie)
大意: 给两个数组$a,b$, 要求重排使得$c_i=a_i\oplus b_i$字典序最小. 字典树上贪心取$n$次, 然后排序, 还不知道怎么证. #include <iostream> ...
- 无重复字符串的最长子串 python
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc&qu ...