多线程核心5:Thread和Object类中线程相关方法

一、wait()、notify()、notifyAll()

  • wait():导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法,或指定的时间已过。 即释放所拥有的monitor,直到重新获得

    注意:wait()要在同步代码块中使用,是为了防止拥有notify()的线程先执行了,导致死锁

  • notify()notifyAll():分别是唤醒单个线程和所有线程

    public class Wait {
          
          
        public static Object object = new Object();
    
        static class Thread1 extends Thread {
          
          
            @Override
            public void run() {
          
          
                synchronized (object){
          
          
                    System.out.println(Thread.currentThread().getName() + "开始执行了");
                    try {
          
          
                        object.wait();
                    } catch (InterruptedException e) {
          
          
                        e.printStackTrace();
                    }
                    System.out.println("线程"+Thread.currentThread().getName() + "获得了锁");
                }
            }
        }
    
        static class Thread2 extends Thread {
          
          
            @Override
            public void run() {
          
          
                synchronized (object) {
          
          
                    object.notify();
                    //为什么还会执行下面这行代码?
                    //notify()唤醒线程时,被唤醒的线程不能马上执行,而要等sychronized()代码块的内容执行完了,才会释放monitor给被唤醒的线程持有
                    System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()");
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
          
          
            Thread1 thread1 = new Thread1();
            Thread2 thread2 = new Thread2();
            thread1.start();
            Thread.sleep(200);
            thread2.start();
        }
    }
    
    ---------------------------
        
    Thread-0开始执行了
    线程Thread-1调用了notify()
    线程Thread-0获得了锁
    
    
    /**
     * thread0.start(),这时thread-0获得monitor,开始执行,
     * 执行到resourceA.wait(),释放持有的monitor
     *
     * thread1.start()与thread0.start几乎同时执行,这时thread-1获得monitor,
     * 开始执行,执行到resourceA.wait(),释放持有的monitor
     *
     * 经过200ms的休眠后,thread-2开始执行,notifyAll,
     * 唤醒thread-0和thread-1,获得monitor的那个线程继续执行完代码
     */
    public class WaitNotifyAll implements Runnable {
          
          
        private static final Object resourceA = new Object();
    
        public static void main(String[] args) throws InterruptedException {
          
          
            WaitNotifyAll r = new WaitNotifyAll();
            Thread thread0 = new Thread(r);
            Thread thread1 = new Thread(r);
            Thread thread2 = new Thread(new Runnable() {
          
          
                @Override
                public void run() {
          
          
                    synchronized (resourceA){
          
          
                        resourceA.notifyAll();
                        System.out.println("Thread2 notified.");
                    }
                }
            });
    
            thread0.start();
            thread1.start();
            Thread.sleep(200);
            thread2.start();
        }
    
        @Override
        public void run() {
          
          
            synchronized (resourceA){
          
          
                System.out.println(Thread.currentThread().getName() + " got resourceA lock.");
                try {
          
          
                    System.out.println(Thread.currentThread().getName() + " waits to start.");
                    resourceA.wait();
                    System.out.println(Thread.currentThread().getName() + "'s waiting to end");
                } catch (InterruptedException e) {
          
          
                    e.printStackTrace();
                }
            }
        }
    }
    
    -----------------------
    Thread-0 got resourceA lock.
    Thread-0 waits to start.
    Thread-1 got resourceA lock.
    Thread-1 waits to start.
    Thread2 notified.
    Thread-1's waiting to end
    Thread-0's waiting to end
    

二、Wait()的过程

在这里插入图片描述

  • 步骤:
    1. 各个线程进入 Entry Set ,开始竞争monitor
    2. 获得锁的线程开始执行
    3. 在运行 wait() 方法后,线程释放锁,没有锁的线程只能等待
    4. 等待的线程被唤醒,然后去竞争 monitor ,即抢锁
    5. 获得锁的再次回到执行状态
    6. 执行结束后,释放锁,退出

三、生产者消费者模式

  • 用wait/notify来实现生产者消费者模式

    public class ProducerConsumerModel {
          
          
        public static void main(String[] args) {
          
          
            EventStorage storage = new EventStorage();
            Producer producer = new Producer(storage);
            Consumer consumer = new Consumer(storage);
            new Thread(producer).start();
            new Thread(consumer).start();
        }
    }
    
    //生产者
    class Producer implements Runnable{
          
          
        private EventStorage storage;
    
        public Producer(EventStorage storage) {
          
          
            this.storage = storage;
        }
    
        @Override
        public void run() {
          
          
            for (int i = 0; i < 100; i++) {
          
          
                storage.put();
            }
        }
    }
    
    //消费者
    class Consumer implements Runnable {
          
          
        private EventStorage storage;
    
        public Consumer(EventStorage storage) {
          
          
            this.storage = storage;
        }
        @Override
        public void run() {
          
          
            for (int i = 0; i < 100; i++) {
          
          
                storage.take();
            }
        }
    }
    
    //仓库
    class EventStorage {
          
          
        private int maxSize;
        private LinkedList<Date> storage;
    
        public EventStorage() {
          
          
            maxSize = 10;
            storage = new LinkedList<>();
        }
    
        //放入产品
        public synchronized void put(){
          
          
            while (storage.size() == 10){
          
          
                try {
          
          
                    wait();
                } catch (InterruptedException e) {
          
          
                    e.printStackTrace();
                }
            }
    
            storage.add(new Date());
            System.out.println("仓库有" + storage.size() + "个产品");
            notify();
        }
    
        //取出产品
        public synchronized void take(){
          
          
            while (storage.size() == 0){
          
          
                try {
          
          
                    wait();
                } catch (InterruptedException e) {
          
          
                    e.printStackTrace();
                }
            }
    
            System.out.println("拿到了" + storage.poll() + ". 仓库还剩下" + storage.size());
            notify();
        }
    }
    

四、交替打印奇偶数

  • 两个线程交替打印0~100的奇偶数

    1. synchronized实现

      /**
       * 一个线程用来打印偶数,一个线程用来打印奇数
       */
      public class WaitNotifyPrintOddEvenSyn {
              
              
      
          private static int count = 0;
          private static final Object lock = new Object();
      
          public static void main(String[] args) {
              
              
              new Thread(new Runnable() {
              
              
                  @Override
                  public void run() {
              
              
                      while (count < 100){
              
              
                          synchronized (lock) {
              
              
                              if ((count & 1) == 0){
              
              
                                  System.out.println(Thread.currentThread().getName() + ":" + count++);
                              }
                          }
                      }
                  }
              }, "偶数:").start();
      
              new Thread(new Runnable() {
              
              
                  @Override
                  public void run() {
              
              
                      while (count < 100){
              
              
                          synchronized (lock) {
              
              
                              if ((count & 1) == 1){
              
              
                                  System.out.println(Thread.currentThread().getName() + ":" + count++);
                              }
                          }
                      }
                  }
              }, "奇数:").start();
          }
      }
      
    2. 使用wait/notify实现

      public class WaitNotifyPrintOddEveWait {
              
              
          private static int count = 0;
          private static final Object lock = new Object();
      
          public static void main(String[] args) {
              
              
              new Thread(new TurningRunner(), "偶数").start();
              new Thread(new TurningRunner(), "奇数").start();
          }
      
          //1. 拿到锁就开始打印
          //2. 打印完就notify其他线程,然后休眠
          static class TurningRunner implements Runnable{
              
              
      
              @Override
              public void run() {
              
              
                  while (count < 100) {
              
              
                      synchronized (lock) {
              
              
                          System.out.println(Thread.currentThread().getName() + ":" + count++);
                          lock.notify();
                          //打印未结束,让出当前线程的锁
                          if (count < 100) {
              
              
                              try {
              
              
                                  lock.wait();
                              } catch (InterruptedException e) {
              
              
                                  e.printStackTrace();
                              }
                          }
                      }
                  }
              }
          }
      }
      

五、sleep()

  • sleep()

    可以让线程进入WAITING状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态

  • wait()与sleep()的异同

    相同:

    1. Wait和sleep方法都可以使线程阻塞,对应线程状态是Waiting或Time_Waiting
    2. wait和sleep方法都可以响应中断Thread.interrupt()

    不同:

    1. wait()方法必须放在同步方法中执行,而sleep()则不用
    2. wait()方法执行会释放锁,而sleep()则不会
    3. sleep方法短暂休眠之后会主动退出阻塞,而没有指定时间的 wait方法则需要被
      其他线程中断后才能退出阻塞。
    4. wait()属于Object类,sleep()属于Thread。wait()、notify()、notifyAll()都属于锁级别的操作,每个锁都绑定一个对象,而不是绑定在线程上,所以每个线程可以拥有多个锁;如果定义在Thread类中会限制锁的灵活使用

六、join()

  • join()

    官方API文档的解释:Waits for this thread to die.

    即新加入的线程先执行,在join的过程中, 主线程的状态是Waiting

    /**
     * 执行thread.join和thread2.join后,main线程开始等待这两个子线程执行完了,再执行
     */
    
    public class Join {
          
          
        public static void main(String[] args) throws InterruptedException {
          
          
            Thread thread = new Thread(new Runnable() {
          
          
                @Override
                public void run() {
          
          
                    try {
          
          
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
          
          
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "执行完毕");
                }
            });
            Thread thread2 = new Thread(new Runnable() {
          
          
                @Override
                public void run() {
          
          
                    try {
          
          
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
          
          
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "执行完毕");
                }
            });
    
            thread.start();
            thread2.start();
            System.out.println("子线程开始执行");
            thread.join();
            thread2.join();
            System.out.println("所有线程执行完毕");
        }
    }
    ------------------------
    子线程开始执行
    Thread-0执行完毕
    Thread-1执行完毕
    所有线程执行完毕
    
  • join()过程中的中断

    /**
     * 执行join的过程中,main线程是在等待子线程执行完毕,所以在join期间中断应该是main线程的中断
     * 在执行过程中,发现在中断信号执行后,子线程依然在执行,这时要thread.interrupt中断子线程
     * 避免主线程和子线程不一致
     */
    public class JoinInterrupt {
          
          
    
        public static void main(String[] args) {
          
          
            Thread mainThread = Thread.currentThread();
            Thread thread = new Thread(new Runnable() {
          
          
                @Override
                public void run() {
          
          
                    try {
          
          
                        mainThread.interrupt();
                        Thread.sleep(5000);
                        System.out.println("Thread0 finished.");
                    } catch (InterruptedException e) {
          
          
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
            System.out.println("等待子线程运行完毕");
            try {
          
          
                thread.join();
            } catch (InterruptedException e) {
          
          
                System.out.println(Thread.currentThread().getName() + "中断了");
                e.printStackTrace();
                //thread.interrupt();
            }
            System.out.println("子线程运行完毕");
        }
    }
    
    

猜你喜欢

转载自blog.csdn.net/weixin_44863537/article/details/112291068
今日推荐