我被线程坑惨了!线程间的通信有毒!!!

Posted Java架构没有996

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我被线程坑惨了!线程间的通信有毒!!!相关的知识,希望对你有一定的参考价值。

在java中,线程间的通信可以使用waitnotifynotifyAll来进行控制。从名字就可以看出来这3个方法都是跟多线程相关的,但是可能让你感到吃惊的是:这3个方法并不是Thread类或者是Runnable接口的方法,而是Object类的3个本地方法。

其实要理解这一点也并不难,调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在作用等同于synchronized(obj){......}的内部才能够去调用obj的wait与notify/notifyAll三个方法,否则就会报错:

java.lang.IllegalMonitorStateException:current thread not owner
也就是说,在调用这3个方法的时候,当前线程必须获得这个对象的锁,那么这3个方法就是和对象锁相关的,所以是属于Object的方法而不是Thread,因为不是每个对象都是Thread。所以我们在理解wait、notify、notifyAll之前,先要了解以下对象锁。

多个线程都持有同一个对象的时候,如果都要进入synchronized(obj){…}的内部,就必须拿到这个对象的锁,synchronized的机制保证了同一时间最多只能有1个线程拿到了对象的锁,如下图:


下面我们来看一下这3个方法的作用:

  • wait:线程自动释放其占有的对象锁,并等待notify
  • notify:唤醒一个正在wait当前对象锁的线程,并让它拿到对象锁
  • notifyAll:唤醒所有正在wait前对象锁的线程

notify和notifyAll的最主要的区别是:notify只是唤醒一个正在wait当前对象锁的线程,而notifyAll唤醒所有。值得注意的是:notify是本地方法,具体唤醒哪一个线程由虚拟机控制;notifyAll后并不是所有的线程都能马上往下执行,它们只是跳出了wait状态,接下来它们还会是竞争对象锁。

下面通过一个常用生产者、消费者的例子来说明。

消息实体类:

package com.podongfeng;

/**
 * Title: Message.class<br>
 * Description: 消息实体<br>
 * Create DateTime: 2016年04月17日 下午1:27 <br>
 *
 * @author podongfeng
 *///加入Java开发交流君样:756584822一起吹水聊天
public class Message {
}

生产者:

package com.podongfeng;

import java.util.ArrayList;
import java.util.List;

/**
 * Title: Producer.class<br>
 * Description: 消息生产者<br>
 * Create DateTime: 2016年04月17日 下午1:28 <br>
 *
 * @author podongfeng
 */
public class Producer extends Thread {

    List<Message> msgList = new ArrayList<>();

    @Override public void run() {
        try {
            while (true) {
                Thread.sleep(3000);
                Message msg = new Message();
                synchronized(msgList) {
                    msgList.add(msg);
                    msgList.notify(); //这里只能是notify而不能是notifyAll,否则remove(0)会报java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }//加入Java开发交流君样:756584822一起吹水聊天

    public Message waitMsg() {
        synchronized(msgList) {
            if(msgList.size() == 0) {
                try {
                    msgList.wait();
                } catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return msgList.remove(0);
        }
    }//加入Java开发交流君样:756584822一起吹水聊天
}

消费者:

package com.podongfeng;

/**
 * Title: Consumer.class<br>
 * Description: 消息消费者<br>
 * Create DateTime: 2016年04月17日 下午1:28 <br>
 *
 * @author podongfeng
 */
public class Consumer extends Thread {

    private Producer producer;

    public Consumer(String name, Producer producer) {
        super(name);
        this.producer = producer;
    }
//加入Java开发交流君样:756584822一起吹水聊天
    @Override public void run() {
        while (true) {
            Message msg = producer.waitMsg();
            System.out.println("Consumer " + getName() + " get a msg");
        }
    }

    public static void main(String[] args) {
        Producer p = new Producer();
        p.start();
        new Consumer("Consumer1", p).start();
        new Consumer("Consumer2", p).start();
        new Consumer("Consumer3", p).start();
    }
}//加入Java开发交流君样:756584822一起吹水聊天

消费者线程调用waitMsg去获取一个消息实体,如果msgList为空,则线程进入wait状态;生产这线程每隔3秒钟生产出体格msg实体并放入msgList列表,完成后,调用notify唤醒一个消费者线程去消费。

最后再次提醒注意:

wait、notify、notifyAll并不是Thread类或者是Runnable接口的方法,而是Object类的3个本地方法。
在调用这3个方法的时候,当前线程必须获得这个对象的锁

生命不止坚毅鱼奋斗,有梦想才是有意义的追求

给大家推荐一个免费的学习交流群:

最后,祝大家早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。

Java开发交流君样:756584822

以上是关于我被线程坑惨了!线程间的通信有毒!!!的主要内容,如果未能解决你的问题,请参考以下文章

实习生跑路留了一个大坑,搞出2个线上问题,我被坑惨了

苹果的策略获利丰厚,却坑惨了黄牛,炒作iPhone14赔惨了

被苹果坑惨了

多线程-线程间的通信

多线程-线程间的通信

被SpringCloud+中间件,坑惨了!