线程之间的通信(一)

简介: 线程之间的通信(一)

一. 等待通知机制的实现#


方法名 作用
wait() 执行当前代码的线程等待
wait(long timeout) timeout时间内若没有其他线程唤醒,也会醒过来
wait(long timeout, int nanos) 超出timeout和额外的时间nanos,没有被其他线程唤醒,也会醒过来
方法名 作用
notify() 随机唤醒一条在等待队列中想去访问同一共享变量的线程
void notifyAll() 唤醒在此对象监视器上等待的所有线程


wait()可以使当前线程停下来,等待某个条件发生变化,并且这个条件超出了当前方法的控制范围,可以实现和自旋一样的效果,但是呢,自旋会比较占用CPU的资源


实例代码:


public class demo1 {
    private static List list = new ArrayList();
    public   void  add(){
        list.add("string");
    }
    public   int size(){
        return list.size();
    }
}


public class demo11 {
    public static void main(String[] args) {
        demo1 demo1 = new demo1();
        Object o = new Object();
        new Thread(()->{
            synchronized (o){
                if(demo1.size()!=5){
                    try {
                        System.out.println("开始等待..."+ System.currentTimeMillis());
                        o.wait();
                        System.out.println("等待结束.."+System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(()->{
            synchronized (o){
                for(int i=0;i<10;i++){
                    demo1.add();
                    if (demo1.size()==5){
                        System.out.println("发出notofy通知...");
                        o.notify();
                    }
                    System.out.println("已经添加了"+i+"个元素");
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
}


运行结果:

开始等待...1549259002081

已经添加了0个元素

已经添加了1个元素

已经添加了2个元素

已经添加了3个元素

发出notofy通知...

已经添加了4个元素

已经添加了5个元素

已经添加了6个元素

已经添加了7个元素

已经添加了8个元素

已经添加了9个元素

等待结束..1549259005086


wait() & notify() & notifyAll()#


wait()总结:


  • wait()方法是Object的方法,作用是让执行当前代码的线程进行等待,(置入到预执行队列),并且会记住当前线程执行到了哪一行代码,当现场被唤醒后,继续从记住的那行代码往下执行
  • wait()方法使用的前提,线程必须获取到对象级别的锁,这也就意味着,wait()必须在synchronized同步方法,或者同步代码块中才能执行
  • 若没有获取到对象锁,抛出异常IllegalMonitorStateExeception
  • 当调用wait()方法后,会立刻释放当前的对象锁


notify()总结:


  • notify()同样是Object的方法,调用此方法的效果是:随机的在唤醒一个等待队列中等待访问同一个共享资源的一个线程
  • notifyAll()唤醒所有,(此时,优先级更高的那个线程,有更大几率先执行,但是也一8I吗确定)
  • notify()使用的前提同样也是线程首先获取到对象级别的锁
  • 调用notify()后,不会立即释放锁,而是继续执行notify()所在的方法,直到此同步方法执行结束后,才会释放对象锁,这也就意味着,notify()之后,wait()状态的线程不会立即被唤醒.


notifyAll()的调用,必须提前获取到锁,而wait()一经调用,立即释放锁,两者不会冲突


二 解决过早通知问题#


  • 试想,如果通知过早,那么就会打乱正常的逻辑,wait()也就没必要执行了,因为它永远都醒不了


public class demo2 {
    String lock = new String("");
    boolean tag = false;
  private Runnable runnableA = new Runnable(){
      @Override
      public void run() {
            synchronized (lock){
                try {
              while(tag==false) {
                  System.out.println("runnableA bagin wait...");
                  lock.wait();
                  System.out.println("RunnableA wait end...");
              }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
      }
  };
private Runnable runnableB = new Runnable(){
    @Override
    public void run() {
        synchronized (lock){
                System.out.println("runnableB bagin notify...");
                lock.notify();
                tag=true;
                System.out.println("RunnableB notify end...");
            System.out.println("不满足条件不唤醒...");
        }
    }
};
public static void main(String[] args) {
    demo2 demo2 = new demo2();
    new Thread(demo2.runnableB).start();
    new Thread(demo2.runnableA).start();
}
}


执行结果:


runnableB bagin notify...  
RunnableB notify end...  
不满足条件不唤醒...


添加了一个判断的条件,实现,若现进行了唤醒,那么不执行wait()


更换两个线程的启动顺序

运行结果:


E:\JavaJDK\bin\java.exe 
runnableA bagin wait...
runnableB bagin notify...
RunnableB notify end...
不满足条件不唤醒...
RunnableA wait end...


三. 等待wait()的条件发生变化与解决#


运行下面的代码:


public class demo33 {
    private String lock;
    public  demo33(String lock){
        this.lock =lock;
    }
    private List list = new ArrayList();
    public void add(){
        synchronized (lock){
            list.add("hello");
            System.out.println(Thread.currentThread().getName()+"add hello 然后唤醒所有wait()线程");
            //唤醒所有
            lock.notifyAll();
        }
    }
    public void subtract(){
        synchronized (lock){
           if(list.size()==0){
               try {
                   System.out.println(Thread.currentThread().getName()+"开始等待");
                   lock.wait();
                   System.out.println(Thread.currentThread().getName() +"等待结束");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
           list.remove(0);
        }
    }
}
 public static void main(String[] args) {
        demo33 demo33 = new demo33("123");
        // 第一条等待的线程
        new Thread(()->{
            demo33.subtract();
        }).start();
        //第二条等待的线程
       new Thread(()->{
            demo33.subtract();
        }).start();
        //唤醒所有
        new Thread(()->{
            demo33.add();
        }).start();
    }


运行结果:


Thread-0开始等待
Thread-1开始等待
Thread-2add hello 然后唤醒所有wait()线程
Thread-1等待结束
Thread-0等待结束
Exception in thread "Thread-0" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
  at java.util.ArrayList.rangeCheck(ArrayList.java:657)


  • 抛出了异常,原始就是因为wait的条件发生了改变,前两条线程因为if(list.size()0)而wait(),紧接着,add()一个元素后,notifyAll()唤醒了所有等待中的线程,于是,那两条等待中的线程在wait()处,继续往下执行remove(0),我们知道,仅仅是添加了一个元素,第二次remove(0)的时候,是非法的,**而在我们的判断if(list.size()0){..}中判断反应不过来**于是抛出了异常
  • 解决方案很简单,既然来不及判断,我们就用while()替换if()这样一来,while()会比if()多执行一次,发现条件满足,继续等待


四. 生产者消费者模式#



1 解决多生产多消费: 操作值 -- 假死现象#


/*
* 多生产,多消费的假死现象
* */
public class demo4 {
private List list = new ArrayList();
public void p(Object o){
    synchronized (o){
        while(!(list.size()==0)){  //对于生产者,list不为空,等待
            System.out.println("生产者"+Thread.currentThread().getName()+"等待了...");
            try {
                o.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 为空,生产
        System.out.println("生产者"+Thread.currentThread().getName()+"生产了...");
        list.add("123");
        o.notify();
    }
}
public void c(Object o){
    synchronized (o){
        while(list.size()==0){  //对于消费者,size==0 等待
            System.out.println("消费者"+Thread.currentThread().getName()+"等待了");
            try {
                o.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("消费者"+Thread.currentThread().getName()+"消费了");
        list.remove(0);
        o.notify();
    }
}
public static void main(String[] args) {
    demo4 demo4 = new demo4();
    Object o = new Object();
    ExecutorService executorService = Executors.newCachedThreadPool();
   for (int i=0;i<5;i++){
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                while (true)
                demo4.p(o);
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                while (true)
                demo4.c(o);
            }
        });
  }
    Thread[] threads = new Thread[2];
    try {
        Thread.sleep(5000);
        System.out.println("主函数醒了");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}


运行上面的代码结果


.
.
.
生产者pool-1-thread-7等待了...
消费者pool-1-thread-10消费了
消费者pool-1-thread-10等待了
消费者pool-1-thread-8等待了
消费者pool-1-thread-6等待了
主函数醒了


卡顿在最后"主函数醒了"不再往下运行,产生了假死的现象实际上还有线程依然存活,只不过是它一直等待,没人唤醒它,notify()会随机唤醒一条线程,这也就意味着,可能存在生产者唤醒的是生产者,消费者唤醒了消费者,导致双方全部等待而造成假死


  • 解决方法,把notify()换成notifyAll()(它不但会唤醒同己,也会唤醒异己)


2 . 一生产与多消费--操作栈,解决wait条件改变与假死问题#

  • 解决wait条件改变---使用while替换if进行判断
  • 假死的原因依然是唤醒了同类---notifyAll()替换notify()



相关文章
|
13天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
1月前
|
Python
如何在Python中实现线程之间的同步和通信?
【2月更文挑战第17天】【2月更文挑战第51篇】如何在Python中实现线程之间的同步和通信?
|
4月前
|
Java
线程间通信之Object.wait/notify实现
线程间通信之Object.wait/notify实现
28 0
|
4月前
|
消息中间件 存储 Unix
进程间通信和线程间通信总结
写在前面 面试的时候一定不要疲劳战,比如上午面了一个,然后中午不休息直接赶到另外一个相距比较远的公司,影响状态。 面试的时候一定不要紧张,不管对方有几个人,总之面试的时候做好充分准备,休息好,放松心态。 好了,言归正传,开始总结。
39 0
|
6月前
|
安全 Java 调度
Java多线程编程的默契对话:线程通信的艺术
Java多线程编程的默契对话:线程通信的艺术
38 0
|
5月前
19.3 Boost Asio 多线程通信
多线程服务依赖于两个通用函数,首先`boost::bind`提供了一个高效的、简单的方法来创建函数对象和函数对象适配器,它的主要功能是提供了一种将函数和它的参数绑定到一起的方法,这种方法可以将具有参数的成员函数、普通函数以及函数对象转化为不带参数的函数对象。当参数绑定后则下一步就需要使用多线程功能,Boost库中提供了`boost::thread`库,`boost::thread`可以用于创建线程、启动线程、等待线程执行结束以及线程间通信等多种操,有了这两个关键库那么我们只需要`accept.accept(*sock)`等待套接字上线,当有套接字上线后则自动创建`MyThread`子线程,
47 0
19.3 Boost Asio 多线程通信
|
4月前
|
安全 Java 数据库连接
详细介绍线程间通信
详细介绍线程间通信 线程间通信是指在多线程编程中,不同的线程之间通过某种方式交换信息的过程。这是一个重要的概念,因为线程之间的协作是实现复杂并发系统的关键。 下面是一些线程间通信的常见方式和示例:
339 0
|
1月前
|
消息中间件 并行计算 网络协议
探秘高效Linux C/C++项目架构:让进程、线程和通信方式助力你的代码飞跃
探秘高效Linux C/C++项目架构:让进程、线程和通信方式助力你的代码飞跃
34 0
|
1月前
|
存储 Java 数据库连接
线程通信(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)
线程通信(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)
34 0
|
1月前
|
安全
多线程通信
多线程通信

热门文章

最新文章

相关实验场景

更多