zoukankan      html  css  js  c++  java
  • (三)(1)线程间通信---wait和notify的使用

    这篇博客记录线程间通信相关api使用以及理解。

    首先第一点,我之前的博客里的线程之间也是通信的,但是他们的通信是建立在访问的是同一个变量上的,相当于是变量、数据层面上的通信,而下面要讲的是线程层面上的通信,这种比前者更加可控。

    Wait和notify机制

    首先明白为什么会出现这个机制。

    目的:举个例子,现在有A,B两个线程,A线程可以不停的改变i的值,B线程再i的值为5时终止。

    方法:为了实现这种效果,我们需要在B线程的run方法之中添加while循环,不停的进行检测i值是否为5,为5则抛出异常停止或者使用stop,interrupt等。

    问题:检测i的值是否为5这个操作,我们称之为轮询,这里的肯定是很耗时很少的,那么就会执行很多次,但是其中有一些检测是没有必要的,浪费了cpu的资源。

    于是乎,就产生了等待/唤醒机制,为了解决cpu资源的浪费,以及让程序更加可控。

    先从字面上简单理解一下:当一个线程执行某个操作但是不满足条件时,先让它等候着,直到条件满足了,再将它唤醒。当然唤醒就是说接着执行相应的操作。

    wait方法:

    让当前线程进行等待,将其加入到预执行队列当中,直到终止或者被唤醒为止,这里的预执行队列就是指处于和其他线程一起竞争获得该锁的状态。并且wait会释放当前的锁,这也就是说没有锁你是不能调用该方法的。

    notify:

    将处于wait状态,且竞争的锁和调用notify方法的线程持有的锁相同的线程唤醒,这个唤醒是随机的,相当于在预执行队列当中随机唤醒一个线程。不过注意notify唤醒并不会立即唤醒,而是将当前同步代码块之中的代码执行结束之后再去唤醒,相当于不会释放锁。

    notifyAll:顾名思义,唤醒依赖于当前锁所有处于wait的线程。

    下面通过一个简单的例子来验证上述结论,就是之前的那个例子,A线程列表元素不为5时wait,B线程负责为5时notify:

    MyList.java:

    package 第三章_wait_join;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MyList {
        private static List<String> list = new ArrayList<String>();
        public static void add(){
            list.add("##");
        }
        public static int getSize(){
            return list.size();
        }
    }

     

    ThreadA.java

    package 第三章_wait_join;
    
    public class ThreadA extends Thread{
        private String lock;
        public ThreadA(String lock){
            this.lock=lock;
        }
        @Override
        public void run(){
            try{
                synchronized(lock){
                    if(MyList.getSize()!=5){  //不为5则wait
                        System.out.println("等待开始...");
                        lock.wait();
                        System.out.println("等待结束...");
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    ThreadB.java

    package 第三章_wait_join;
    
    public class ThreadB extends Thread{
        private String lock;
        public ThreadB(String lock){
            this.lock=lock;
        }
        @Override
        public void run(){
            try{
                synchronized(lock){
                    for(int i=0;i<10;i++){
                        MyList.add();
                        if(MyList.getSize()==5){
                            System.out.println("发出通知");
                            lock.notify();
                        }
                        System.out.println("添加了"+(i+1)+"个元素");
                        Thread.sleep(10);
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    test.java:

    package 第三章_wait_join;
    
    public class test {
        public static void main(String[] args){
            try {
                ThreadA A = new ThreadA("lock");
                ThreadB B = new ThreadB("lock");
                A.start();
                Thread.sleep(50);
                B.start();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    运行结果:

    可以看出来,A线程开始等待之后就释放lock锁,B线程获取到了该锁,执行代码,添加了5个元素时,发出了通知,但是发出通知之后,它没有释放锁,而是将同步代码块执行完,然后再释放锁,A线程获取到,执行wait下面的代码。

    说明两点:

     1.另外和之前一样,如果一个线程已经处于阻塞状态了,那么就不能再调用其他会产生阻塞的方法,比如调用了wait就不能调用interrupt,suspend,否则会产生异常,你无法阻塞一个已经被阻塞的线程。

    2.前面的wait都是没有参数的,wait(long)就是说在long长时间之内,如果没有被唤醒,那么就自动唤醒该线程,很好理解,

    通知过早

    那么wait,notify肯定也是有一定顺序的,你不能还没有wait就notify,那么是不会notify任何线程的,这也叫做通知过早。看下面的例子:

    更改之前的test.java

    package 第三章_wait_join;
    
    public class test {
        public static void main(String[] args){
            try {
                ThreadA A = new ThreadA("lock");
                ThreadB B = new ThreadB("lock");
                B.start();
                Thread.sleep(1000);
                A.start();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    运行结果:

    可以看到虽然发出了通知,但是这个等待永远不会结束,因为你在发出通知的时候线程还没有处于阻塞状态,而是处于就绪状态,notify并不会唤醒任何线程。

     

  • 相关阅读:
    S5P4418iNand清空方法
    使用 GIT 获得Linux Kernel的代码并查看,追踪历史记录
    Linux3.4内核的基本配置和编译
    uboot---linux
    TestNG的简单使用
    java selenium webdriver处理JS操作窗口滚动条
    testNG入门详解
    零成本实现接口自动化测试 – Java+TestNG 测试Restful service
    Selenium Webdriver——操作隐藏的元素(二)display属性
    python selenium webdriver处理浏览器滚动条
  • 原文地址:https://www.cnblogs.com/eenio/p/11384099.html
Copyright © 2011-2022 走看看