博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
5、分析一个常见的java多线程通信问题(假死现象)
阅读量:4035 次
发布时间:2019-05-24

本文共 2530 字,大约阅读时间需要 8 分钟。

一件复杂的事,一个人如果不能做,两个人又做的不好,一群人就可能很好的解决了。对于线程来说也是,通过多个线程就能完成一个更复杂的功能,这就需要多个线程协作,协作就需要交流,但是交流总是会出问题的。在这篇文章中我们分析一下java多线程通信过程中出现的一个假死现象。然后给出一个解决办法。

一、假死现象重现

为了更好地演示我们的实例,我们使用生产者消费者模式,一边生产一边消费。

在这里插入图片描述

下面我们先试着实现一下。这个例子的功能描述如下:

有一个产品a,生产方法生产a,消费方法消费a。然后10个生产线程生产,10个消费线程消费。永不停息。=

在这里插入图片描述
上面的流程很清晰,一堆线程生产,生产了之后notify消费者去消费。

第一步:定义变量

public class ProduceAndConsumerModel {
//a表示共享变量 private int a = 0; //lock就是一把锁 final private Object lock = new Object(); // isProduced表示是否已经生产的标志 private volatile boolean isProduced = false;}

第二步:生产和消费方法(定义在上面的类中)

首先是生产方法

public void produce() throws InterruptedException {
synchronized (lock) {
// 如果已经生产了,那就等消费了再生产 if (isProduced) {
lock.wait(); } else {
// 没有生产,那就生产一个,并通知消费者去消费 a++; System.out.println("生产者" + Thread.currentThread().getName() + "生产一个产品:" + (a)); lock.notify(); isProduced = true; } }}

然后是消费方法

public void consume() throws InterruptedException {
synchronized (lock) {
// 如果有产品,那就消费,并通知生产者可以继续生产了。 if (isProduced) {
System.out.println("消费者" + Thread.currentThread().getName() + "消费一个产品:" + (a)); lock.notify(); isProduced = false; } else {
// 如果没有产品,那就等待一会 lock.wait(); } }}

第三步:测试

public class ProduceAndConsumerModel {
public static void main(String[] args) {
ProduceAndConsumerModel model = new ProduceAndConsumerModel(); // 生产线程一直不停的生产 for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
while (true) {
model.produce(); } }; }.start(); } // 消费线程一直不停的消费 for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
while (true) {
model.consume(); } }; }.start(); } }}

上面这个例子的功能,在一开始也已经说明了,这里produce和consume方法中,使用的是wait/notify机制来实现的,我们运行一下看会出现什么结果:

在这里插入图片描述

我们看到,本来整个程序是永不停歇的,但是在生产了6个产品之后,突然间就停歇了,也就是我们今天的主题,多线程通信出现了假死状态。为什么会出现这种现象呢?我们来分析一下原因。

二、假死状态分析

其实出现这个现象的原因很简单,那就是和我们的wait/notify机制有关,我们几句话来总结一下:

“假死”的现象就是全部线程都进入了WAITING状态(死锁),则程序就不再执行任何业务功能了,整个项目呈停止状态。上面的案例中出现假死的现象是由于仅仅唤醒了同类(生产者唤醒了生产者,消费者唤醒了消费者)的现象大量出现导致的。

下面我们画一张图来分析一下:

在这里插入图片描述

也就是说notify通知的是是同类。造成了这种堵塞现象。这是其根本原因,而且这张图是我们自己画的。下面我们就直接使用jstack工具来分析一下线程的状态。这两个工具是jdk自带的,我们可以直接使用。

第一步:使用jps查看当前电脑存在的所有java线程

在这里插入图片描述

第二步:使用jstack工具查看线程状态信息

在这里插入图片描述

现在我们知道了,目前所有的线程都是出于等待的状态,这也就是假死现象的验证。

假死现象的原因我们知道了,那么我们如何改正呢?

三、假死状态修复

假死现象的改正其实很简单,网上的方式也很多,比如说通过BlockingQueue或者是notifyAll方法。notifyAll方法超级简单,就是把上面produce和consume方法中的notify改成notifyAll方法即可。目的就是通知到所有的其他线程,生产线程该生产的生产,消费线程该消费的消费。

对于java多线程的系列的文章,这算是基础入门。还有更多文章我也经持续发布。感谢支持。OK,今天的文章先到这。

在这里插入图片描述

转载地址:http://enbdi.baihongyu.com/

你可能感兴趣的文章
IP报文格式学习笔记
查看>>
autohotkey快捷键显示隐藏文件和文件扩展名
查看>>
Linux中的进程
查看>>
学习python(1)——环境与常识
查看>>
学习设计模式(3)——单例模式和类的成员函数中的静态变量的作用域
查看>>
自然计算时间复杂度杂谈
查看>>
当前主要目标和工作
查看>>
Intellij IDEA启动优化,让开发的感觉飞起来
查看>>
使用 Springboot 对 Kettle 进行调度开发
查看>>
如何优雅的编程,lombok你怎么这么好用
查看>>
一文看清HBase的使用场景
查看>>
除了负载均衡,Nginx还可以做很多,限流、缓存、黑白名单
查看>>
解析zookeeper的工作流程
查看>>
搞定Java面试中的数据结构问题
查看>>
慢慢欣赏linux make uImage流程
查看>>
linux内核学习(7)脱胎换骨解压缩的内核
查看>>
以太网基础知识
查看>>
慢慢欣赏linux 内核模块引用
查看>>
kprobe学习
查看>>
慢慢欣赏linux phy驱动初始化2
查看>>