博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程 等待/通知机制的实现
阅读量:4229 次
发布时间:2019-05-26

本文共 6222 字,大约阅读时间需要 20 分钟。

等待/通知机制的实现
1. 概述
    * 方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,
        该方法用来将当前线程置入"预执行队列"中,并且在wait()所在的代码行处停止执行,
        直到接到通知或被中断为止。在调用wait()之前,线程必须获得该对象的对象级别锁,
        即只能在同步方法或同步块中调用wait()方法。在执行wait()方法后,当前对象释放锁。
    * 方法notify()也要在同步方法或者同步块中调用,即在调用前,线程也必须获得该对象的对象
    级别锁。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由
    线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象
    的对象锁。
        注意:在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能
            马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized
            代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。
    * 总结
        wait使线程停止运行,而notify使停止的线程继续运行。
2. notify()方法后当前线程还是会继续执行完:
    eg:
        public class MyList {
        private static List<String> list=new ArrayList<>();
        public static void add(){
            list.add("anyString");
        }
        public static int size(){
            return list.size();
        }
    }
        public class ThreadA extends Thread{
        private Object lock;
        public ThreadA(Object lock){
            super();
            this.lock=lock;
        }
        @Override
        public void run() {
            try{
                synchronized (lock){
                    if(MyList.size()!=5){
                        System.out.println("wait begin "+System.currentTimeMillis());
                        lock.wait();
                        System.out.println("wait end "+System.currentTimeMillis());
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
        public class ThreadB extends Thread{
        private Object lock;
        public ThreadB(Object lock){
            super();
            this.lock=lock;
        }
        @Override
        public void run() {
            try{
                synchronized (lock){
                    for(int i=0;i<10;i++){
                        MyList.add();
                        if(MyList.size()==5){
                            lock.notify();
                            System.out.println("已发出通知!");
                        }
                        System.out.println("添加了"+(i+1)+"个元素!");
                        Thread.sleep(1000);
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
        public class Run {
        public static void main(String[] args){
            try{
                Object lock=new Object();
                ThreadA a=new ThreadA(lock);
                a.start();
                Thread.sleep(50);
                ThreadB b=new ThreadB(lock);
                b.start();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    结果:
        wait begin 1503146432420
        添加了1个元素!
        添加了2个元素!
        添加了3个元素!
        添加了4个元素!
        已发出通知!
        添加了5个元素!
        添加了6个元素!
        添加了7个元素!
        添加了8个元素!
        添加了9个元素!
        添加了10个元素!
        wait end 1503146442540
3. 方法wait()锁释放与notify()锁不释放
    wait()方法执行到时,该线程就会释放锁,notify()方法执行时,线程不会释放锁,并且代码会执行完毕,执行完毕后才释放锁。
4. 当interrupt方法遇到wait方法
    当线程wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常。
    eg:
        public class Service {
        public void testMethod(Object lock){
            try{
                synchronized (lock){
                    System.out.println("begin wait()");
                    lock.wait();
                    System.out.println("  end wait()");
                }
            }catch (InterruptedException e){
                e.printStackTrace();
                System.out.println("出现异常了,因为呈wait状态的线程被interrupt了!");
            }
        }
    }
        public class ThreadA extends Thread {
        private Object lock;
        public ThreadA(Object lock){
            super();
            this.lock=lock;
        }
        @Override
        public void run() {
            Service service=new Service();
            service.testMethod(lock);
        }
    }
        public class Test {
        public static void main(String[] args){
            try{
                Object lock=new Object();
                ThreadA a=new ThreadA(lock);
                a.start();
                Thread.sleep(5000);
                a.interrupt();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
        效果:
        begin wait()
        出现异常了,因为呈wait状态的线程被interrupt了!
        java.lang.InterruptedException
            at java.lang.Object.wait(Native Method)
            at java.lang.Object.wait(Object.java:502)
            at org.fkit.six.Service.testMethod(Service.java:11)
            at org.fkit.six.ThreadA.run(ThreadA.java:16)
        * 从上可以看出:
            * 在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。
            * 在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程
                会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒。
5. 只通知一个线程
    调用方法notify()一次只随机通知一个线程进行唤醒
6. 唤醒所有线程
    使用notifyAll()方法可以唤醒所有该锁上的线程
7. 方法wait(long)的使用
    带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒,
        如果超过这个时间则自动唤醒。
8. 等待wait的条件发生变化(难点哦)
    * 在使用wait/notify模式时,还需要注意另外一种情况,也就是wait等待的条件发生了变化,
        也容易造成程序逻辑的混乱。
        eg:
                public class ValueObject {
                public static List<String> list=new ArrayList<>();
            }
                public class Add {
                private String lock;
                public Add(String lock){
                    super();
                    this.lock=lock;
                }
                public void add(){
                    synchronized (lock){
                        ValueObject.list.add("anyString");
                        lock.notifyAll();
                    }
                }
            }
                public class Subtract {
                    private String lock;
                    public Subtract(String lock){
                        super();
                        this.lock=lock;
                    }
                    public void subtract(){
                        try{
                            synchronized (lock){
                                if(ValueObject.list.size()==0){
                                    System.out.println("wait begin ThreadName="+Thread.currentThread().getName());
                                    lock.wait();
                                    System.out.println("wait  end ThreadName="+Thread.currentThread().getName());
                                }
                                ValueObject.list.remove(0);
                                System.out.println("list size="+ValueObject.list.size());
                            }
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                }
                public class ThreadSubtract extends Thread{
                private Subtract r;
                public ThreadSubtract(Subtract r){
                    super();
                    this.r=r;
                }
                @Override
                public void run() {
                    r.subtract();
                }
            }
                public class ThreadAdd extends Thread {
                private Add p;
                public ThreadAdd(Add p){
                    super();
                    this.p=p;
                }
                @Override
                public void run() {
                    p.add();
                }
            }
                public class Run {
                public static void main(String[] args) throws InterruptedException{
                    String lock=new String("");
                    Add add=new Add(lock);
                    Subtract subtract=new Subtract(lock);
                    ThreadSubtract subtract1Thread=new ThreadSubtract(subtract);
                    subtract1Thread.setName("subtract1Thread");
                    subtract1Thread.start();
                    ThreadSubtract subtract2Thread=new ThreadSubtract(subtract);
                    subtract2Thread.setName("subtract2Thread");
                    subtract2Thread.start();
                    Thread.sleep(1000);
                    ThreadAdd addThread=new ThreadAdd(add);
                    addThread.setName("addThread");
                    addThread.start();
                }
            }
            效果:
            wait begin ThreadName=subtract1Thread
            wait begin ThreadName=subtract2Thread
            wait  end ThreadName=subtract2Thread
            Exception in thread "subtract1Thread" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
            list size=0
                at java.util.ArrayList.rangeCheck(ArrayList.java:653)
            wait  end ThreadName=subtract1Thread
                at java.util.ArrayList.remove(ArrayList.java:492)
                at org.fkit.seven.Subtract.subtract(Subtract.java:20)
                at org.fkit.seven.ThreadSubtract.run(ThreadSubtract.java:15)
            分析:因为我们有两个线程在等待删除,而插入只执行了一次,却唤醒了两个线程,去删除的时候当然会出现错误,
                做如下修改就可以解决问题
                    public class Subtract {
                    private String lock;
                    public Subtract(String lock){
                        super();
                        this.lock=lock;
                    }
                    public void subtract(){
                        try{
                            synchronized (lock){
                                while(ValueObject.list.size()==0){
                                    System.out.println("wait begin ThreadName="+Thread.currentThread().getName());
                                    lock.wait();
                                    System.out.println("wait  end ThreadName="+Thread.currentThread().getName());
                                }
                                ValueObject.list.remove(0);
                                System.out.println("list size="+ValueObject.list.size());
                            }
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                }
                分析:现在来考虑这个问题,当我们线程被唤醒成功的时候,不是立即去执行删除操作,而是回头再去判断一次,因为线程
                    被同步了,顺序执行,当第二个线程执行到的时候就已经为0了,重新进入等待,而不会去执行删除,就避免了这种错误。

转载地址:http://ymjqi.baihongyu.com/

你可能感兴趣的文章
UVM:8.2.3 复杂的重载
查看>>
UVM:8.2.4 factory 机制的调试
查看>>
UVM:8.3.1 重载transaction
查看>>
UVM:8.3.2 重载sequence
查看>>
leetcode171.[math] Excel Sheet Column Number
查看>>
Log4j配置
查看>>
java发送https请求证书问题
查看>>
js新消息提醒
查看>>
js窗体消息提醒
查看>>
深入Hibernate映射文件(二)——<hibernate-mapping>的属性
查看>>
详解在Spring中进行集成测试
查看>>
Struts2中过滤器和拦截器的区别
查看>>
51单片机:led灯闪烁10次后熄灭
查看>>
安卓:okhttp请求,获取返回数据
查看>>
安卓:股票筛选及分析系统
查看>>
增加windows下Tomcat运行时的内存
查看>>
tomcat群集中session共享的几个方案
查看>>
查找google谷歌北京IP地址的方法
查看>>
本科大数据专业该怎么上?
查看>>
云创大数据1+X大数据应用部署与调优职业技能等级证书预申报正式开启!
查看>>