四、synchronized、nodify的bug案例

1、生产者消费者模式使用nodify的弊端-死锁

该实例中消费者吃完了后本来是唤醒生产者生产油条 但把另一个消费者唤醒了,然后被唤醒的消费者开始吃 发现没有了就开始wait而此时生产者并没有被唤醒也在wait 就导致大家都在wait 导致死锁
下面代码为死锁情况

package ProducerAndConsumer;

/**
 * 死锁了
 * 生产者消费者模式使用nodify的弊端:
 * 有可能会唤醒本类的线程即:
 * 在多生产这多消费者的时候会出现
 * 消费者唤醒消费者,生产者唤醒生产者而的情况。
 *
 * 吃完了后本来是唤醒生产者生产油条 但把另一个消费者唤醒了,然后被唤醒的消费者开始吃 发现没有了就开始wait
 * 而此时生产者并没有被唤醒也在wait 就导致大家都在wait 导致死锁
 * @author xzq
 */
public class ProducerAndConsumerTest {


    public static void main(String[] args) {
        ShareData data=new ShareData();

        new Producer2(data,"生产者01").start();
        new Producer2(data,"生产者02").start();
        new Consumer2(data,"消费者01").start();
        new Consumer2(data,"消费者02").start();

    }

}


class  ShareData {

    private String  name;

    private int count=0;

    private int number=1;

    private static final int MAX=1;


    //生产
    public synchronized void product(String name) throws InterruptedException{
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName+":要开始生产了……");
        //如果等于最大就等待
        while(count==MAX){
            System.out.println(threadName+":已经达到最大,等着消费者消费……");
            this.wait();
            Thread.sleep(10);
        }
        //如果没有达到最大,就生产
        this.name="第"+number+"号"+name;
        count++;
        number++;
        System.out.println(threadName+":已经做好了"+this.name+"啦!");
        /*
         * 做好一个就唤醒消费者进行消费
         * 注意这里的nodify不一定唤醒的是消费者
         */
        this.notify();//会导致死锁
        // this.notifyAll(); //不会死锁

    }


    //消费
    public synchronized void consume() throws InterruptedException{
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName+":要开始消费了……");
        //如果没有了,就等待
        while(count==0){
            this.wait();
            Thread.sleep(10);
        }
        //如果达到最大就消费
        count--;
        System.out.println(threadName+":吃掉了"+name);
        /*
         * 没有了就唤醒生产者可以继续生产了
         * 但是唤醒的不一定是生产者
         *
         * 吃完了后本来是唤醒生产者生产油条 但把另一个消费者唤醒了,然后被唤醒的消费者开始吃 发现没有了就开始wait
         * 而此时生产者并没有被唤醒也在wait 就导致大家都在wait 导致死锁
         */
        this.notify();//会导致死锁
        //this.notifyAll(); //不会死锁
    }

}

class  Producer2 extends Thread{

    private ShareData  data;

    public Producer2(ShareData data,String name) {
        super(name);
        this.data=data;
    }

    @Override
    public void run() {
        try {
            //每个进入的线程不停的生产,循环判断是否生产
            for (; ;) {
                data.product("油条");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class  Consumer2 extends Thread{
    private ShareData  data;

    public Consumer2(ShareData data,String name) {
        super(name);
        this.data=data;
    }

    @Override
    public void run() {
        try {
            //消费10根油条
            for (; ; ) {
                data.consume();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

吃完了后本来是唤醒生产者生产油条 但把另一个消费者唤醒了,然后被唤醒的消费者开始吃 发现没有了就开始wait 而此时生产者并没有被唤醒也在wait 就导致大家都在wait 导致死锁

在这里插入图片描述
如果使用notifyAll()就不会导致死锁,可以全部唤醒,下面是运行结果在这里插入图片描述

2、对run方法加上synchronized是否能达到同步效果

观察继承Thread类的时候,对run方法加上synchronized是否能达到同步效果
使用继承Thread类的方式在run方法上加synchronized达不到同步的效果,因为每个线程锁的都是自己,而这每个线程又是不同的对象,所以达不到同步效果。

package day03;

/**
 * 观察继承Thread类的时候,
 * 对run方法加上synchronized是否能达到同步效果
 *
 * 使用继承Thread类的方式在run方法上加synchronized
 * 达不到同步的效果,因为每个线程锁的都是自己,而这
 * 每个线程又是不同的对象,所以达不到同步效果。
 * @author xzq
 */
public class WatchThread {
    public static void main(String[] args) {

        for (int i = 1; i <= 3; i++) {
            MyThread mt = new MyThread("线程"+i);
            //设置线程的名称,方便查看在执行哪个对象的run()
            mt.start();
        }
    }

    static class MyThread extends Thread{

        public MyThread(String name) {
            super(name);
        }

        @Override
        public synchronized void run() {
            String threadName = Thread.currentThread().getName();
            /*
            如果像下面synchronized (WatchThread.class)加 三个线程就会依次执行,
            new 了三个MyThread对象,每个对象都有自己的run方法
             */
//          synchronized (WatchThread.class) {
            //循环10次,每1秒钟循环一次
            for (int i = 1; i <= 10; i++) {
                System.out.println(threadName+":执行第"+i+"次循环……");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //synchronized结束
//          }

        }
    }
}


在这里插入图片描述
new 了三个MyThread对象,每个对象都有自己的run方法,所以达不到同步的目的,将run里的逻辑放在synchronized (WatchThread.class){ }代码块中可以解决问题

        @Override
        public /*synchronized*/ void run() {
            String threadName = Thread.currentThread().getName();
            /*
            如果像下面synchronized (WatchThread.class)加 三个线程就会依次执行,
            new 了三个MyThread对象,每个对象都有自己的run方法
             */
          synchronized (WatchThread.class) {
            //循环10次,每1秒钟循环一次
            for (int i = 1; i <= 5; i++) {
                System.out.println(threadName+":执行第"+i+"次循环……");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //synchronized结束
          }

        }

结果如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值