java多线程通讯问题(死锁)

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

一、假死现象重现

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

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

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

上面的流程很清晰,一堆线程生产,生产了之后notify消费者去消费。上面的图指的是一堆线程生产,一堆线程消费,可能箭头画的指示的一个。

代码实现:

1.

package com.springboot;

/**
 *生产者消费者demo
 */
public class ProductAndConsumer {

//    共享变量
    private int SharedVariable = 0;
//    lock就是一把锁
    private  Object lock = new Object();
    //是否生产
    private volatile boolean isProducted = false;

    /**
     * 生产方法
     * @throws InterruptedException
     */
    public  void produce() throws  InterruptedException{
        synchronized (lock){
            //如果已经生产,那就等消费了在生产
            if(isProducted) {
                lock.wait();
            }else {
                SharedVariable++;
                System.out.println("生产者:"+Thread.currentThread().getName()+",生产一个产品"+SharedVariable);
               //唤醒消费线程
                lock.notify();
                isProducted=true;
            }
        }
    }

    /**
     * 消费方法
     * @throws InterruptedException
     */
    public   void conusmer() throws  InterruptedException{
        synchronized (lock){
            //如果已经生产,那就等消费了在生产
            if(!isProducted) {
                lock.wait();
            }else {
//                SharedVariable--;
                System.out.println("消费者:"+Thread.currentThread().getName()+",消费一个产品:"+SharedVariable);
                //唤醒生产线程去生产
                lock.notify();
                isProducted=false;
            }
        }
    }

    public static void main(String[] args) {

        final ProductAndConsumer proAndCon = new ProductAndConsumer();

        for(int j=0;j<10;j++)
        {
            new Thread(){
                //                @Override
                public void run() {
//                    super.run();
                    while (true) {
                        try {

                            proAndCon.produce();
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }.start();
        }

            //消费线程一直消费
            for(int i=0;i<10;i++)
            {
                new Thread(){
                    public void run() {
                        while (true) {
                            try {

                                proAndCon.conusmer();
                            } catch (InterruptedException e) {
                            }
                        }
                    }
                }.start();
            }

    }
}

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

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

二、假死状态分析

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

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

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

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

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

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

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

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

三、假死状态修复

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

如下图:

这样问题就解决了

多线程学习,详见博客:

https://www.cnblogs.com/BigJunOba/p/8980007.html

发布了8 篇原创文章 · 获赞 4 · 访问量 1779

猜你喜欢

转载自blog.csdn.net/zprwcc000/article/details/101689511