美文网首页
Java多线程学习三 wait()与notify()

Java多线程学习三 wait()与notify()

作者: 殷俊杰 | 来源:发表于2019-04-18 15:45 被阅读0次

    线程间通信使用wait()和notify()方法,这两个方法都是Object类的方法,调用时线程必须持有该对象的对象级别锁,如果调用时没有持有适当的锁,会抛出IllegalMonitorStateException。

    • 调用wait()方调用线程的interrupt()方法会出现InterruptedException异常
    • wait(long)方法的功能是等法后,当前线程会释放持有的锁进入等待状态
    • notify()方法可以唤醒一个因调用了wait操作而处于等待状态中的线程,使其进入就绪状态,加入争抢锁的队列,notify()方法仅通知“一个”线程,且不会立即释放当前锁。
    • 当线程呈wait()状态时,待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒

    1. 用法示例

    public class Thread1 extends Thread {
        private Object lock;
    
        public Thread1(Object lock) {
            this.lock = lock;
        }
    
        @Override
        public void run() {
            try {
                synchronized (lock){
                    System.out.println("开始 wait time="+System.currentTimeMillis());
                    lock.wait();
                    System.out.println("结束 wait time="+System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public class Thread2 extends Thread {
        private Object lock;
    
        public Thread2(Object lock) {
            this.lock = lock;
        }
    
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println("开始 notify time=" + System.currentTimeMillis());
                lock.notify();
                System.out.println("结束 notify time=" + System.currentTimeMillis());
            }
        }
    }
    
    public class TestMain {
        public static void main(String[] args) throws InterruptedException {
            Object lock=new Object();
            Thread1 thread1=new Thread1(lock);
            Thread2 thread2=new Thread2(lock);
            thread1.start();
            Thread.sleep(3000);
            thread2.start();
        }
    }
    
    

    2. 等待wait的条件发生变化

    public class Add extends Thread{
        private List list;
        private Object lock;
        public Add(List list,Object lock) {
            this.list = list;
            this.lock=lock;
        }
    
    
        @Override
        public void run() {
            synchronized (lock){
                list.add("1");
                lock.notifyAll();
            }
    
        }
    }
    
    
    public class Sub extends Thread{
        private List list;
        private Object lock;
    
        public Sub(List list, Object lock) {
            this.list = list;
            this.lock = lock;
        }
    
        @Override
        public void run() {
            synchronized (lock){
                try {
                    if (list.size()==0){
                        System.out.println("wait begin "+Thread.currentThread().getName());
                        lock.wait();
                        System.out.println("wait end "+Thread.currentThread().getName());
                    }
                    list.remove(0);
                    System.out.println("list size="+list.size());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
        public static void main(String[] args) throws InterruptedException {
            String lock="";
            List list=new ArrayList();
            Add add=new Add(list,lock);
            add.setName("add");
            Sub sub1=new Sub(list,lock);
            sub1.setName("sub1");
            Sub sub2=new Sub(list,lock);
            sub2.setName("sub2");
    
            sub1.start();
            sub2.start();
            Thread.sleep(1000);
            add.start();
    
        }
    
    wait begin sub1
    wait begin sub2
    wait end sub2
    Exception in thread "sub1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    list size=0
    wait end sub1
        at java.util.ArrayList.rangeCheck(ArrayList.java:657)
        at java.util.ArrayList.remove(ArrayList.java:496)
        at com.yjj.chapter03.test02_notify2.Sub.run(Sub.java:29)
    
    

    当sub1去删除时,sub2已经把list里的值删了,此时list的size是0,再去删除就会报索引越界错误,解决办法就是if改为while

    3. 生产者消费者实现

    public class Consumer {
        private String lock;
    
        public Consumer(String lock) {
            this.lock = lock;
        }
    
        public void getValue() {
            synchronized (lock) {
                try {
    //                Thread.sleep(1000);
                    if (ValueObject.value.equals("")) {
                        System.out.println(Thread.currentThread().getName()+"waiting");
                        lock.wait();
    //                    System.out.println("get的值是" + ValueObject.value);
                    }
                    ValueObject.value = "";
                    lock.notify();
                    System.out.println(Thread.currentThread().getName()+"notify");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    public class Productor {
        private String lock;
    
        public Productor(String lock) {
            this.lock = lock;
        }
    
        public void setValue(){
            try{
    //            Thread.sleep(1000);
                synchronized (lock){
                    if (!ValueObject.value.equals("")){
                        System.out.println(Thread.currentThread().getName()+"waiting");
                        lock.wait();
                    }
                    String value=System.currentTimeMillis()+"_"+System.nanoTime();
    //                System.out.println("set的值是"+value);
                    ValueObject.value=value;
                    lock.notify();
                    System.out.println(Thread.currentThread().getName()+"notify");
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    
    
    public class ThreadP extends Thread {
        private Productor productor;
    
        public ThreadP(Productor productor) {
            this.productor = productor;
        }
    
        @Override
        public void run() {
            while (true) {
                productor.setValue();
            }
        }
    }
    
    
    public class ThreadC extends Thread {
        private Consumer consumer;
    
        public ThreadC(Consumer consumer) {
            this.consumer = consumer;
        }
    
        @Override
        public void run() {
            while (true) {
                consumer.getValue();
            }
        }
    }
    
    
    public class TestMain {
        public static void main(String[] args) throws InterruptedException {
            String lock=new String("");
            Productor productor=new Productor(lock);
            Consumer consumer=new Consumer(lock);
            ThreadC threadC=new ThreadC(consumer);
            threadC.setName("C");
            ThreadP threadP=new ThreadP(productor);
            threadP.setName("p");
            threadC.start();
            threadP.start();
        }
    }
    
    

    4. join()方法

        @Override
        public void run() {
            try {
                int second=(int) (Math.random()*1000);
                Thread.sleep(second);
                System.out.println("线程执行完了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread=new MyThread();
            myThread.start();
            //Thread.sleep(??); //睡多少秒好呢,我们可以使用join来实现
            myThread.join();
            System.out.println("我想等myThread执行完毕后再执行");
        }
    

    join()方法的作用是使当前所在的线程无限阻塞,直到调用join方法的线程执行完毕销毁后再继续执行。
    方法join具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理作为同步。

    5. join()遇到interrupt()

    线程B启动一个线程A并join到线程A,等线程A执行完才继续执行,线程A做一个耗时巨长的事情,线程C执行线程B interrupt(),此时线程B会报异常,,,算了,上代码吧talk is cheap show my code

    public class ThreadA extends Thread{
    
        @Override
        public void run() {
            for (int i=0;i<Integer.MAX_VALUE;i++){
                String str=String.valueOf(Math.random());
            }
        }
    }
    
    public class ThreadB extends Thread{
    
        @Override
        public void run() {
            try {
                ThreadA threadA=new ThreadA();
                threadA.start();
                threadA.join();
                System.out.println("线程B执行完毕");
            } catch (InterruptedException e) {
                System.out.println("线程B被中断了");
                e.printStackTrace();
            }
    
        }
    }
    
    public class ThreadC extends Thread{
    
        private ThreadB threadB;
    
        public ThreadC(ThreadB threadB) {
            this.threadB = threadB;
        }
    
        @Override
        public void run() {
           threadB.interrupt();
        }
    }
    
    public class TestMain {
        public static void main(String[] args) throws InterruptedException {
            ThreadB threadB=new ThreadB();
            threadB.start();
            ThreadB.sleep(1000);
            ThreadC threadC=new ThreadC(threadB);
            threadC.start();
        }
    }
    
    java.lang.InterruptedException
    线程B被中断了
        at java.lang.Object.wait(Native Method)
        at java.lang.Thread.join(Thread.java:1252)
        at java.lang.Thread.join(Thread.java:1326)
        at com.yjj.chapter03.test05_join_interrupt.ThreadB.run(ThreadB.java:16)
    

    可以看到,在我们调用join方法时,编译器要求我们必须要处理一个检查异常,以防止join时该线程被中断

    6.join(long)与sleep(long)的区别

    join(long)在内部其实是使用wait(long)来实现的,所以你懂得,wait是释放锁的,而sleep是不释放锁的。

    7. ThreadLocal

    感觉也应该单拿出来讲

    相关文章

      网友评论

          本文标题:Java多线程学习三 wait()与notify()

          本文链接:https://www.haomeiwen.com/subject/uudjoftx.html