1、为什么通信的方法wait()、nofity()、notifyAll()被定义在Object类里,而sleep被定义在Thread类里?
2、用三种方式实现生产者模式
3、join、sleep和wait期间,线程的状态分别是什么?为什么?
1、作用、用法
阻塞阶段:
使用wait()后进入阻塞阶段,以下四种情况之一发生,才会被唤醒 复制代码
唤醒阶段
遇到中断
//普通情况 package com.company; public class WaitAndNotify { public static Object object = new Object(); static class WaitRun implements Runnable { @Override public void run() { synchronized (object) { System.out.println(System.currentTimeMillis() + ": " + Thread.currentThread().getName() + "开始执行了"); //wait后丢掉锁 try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } //如果再次获得锁才会执行接下来的代码 System.out.println(System.currentTimeMillis() + ": " + Thread.currentThread().getName() + "又重新获取到了锁"); } } } static class NotifuRun implements Runnable { @Override public void run() { synchronized (object) { object.notify(); System.out.println(Thread.currentThread().getName() + "调用了notify(),"); //为了看使用了notify后是立即释放锁,还是等执行完成后 释放锁 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new WaitRun()); Thread t2 = new Thread(new NotifuRun()); t1.start(); //为了让t1先使用到了wait进入到阻塞态 Thread.sleep(100); t2.start(); } } 复制代码
从结果可以看出来,线程0的执行顺序是先wait,再被notify后,重新获得锁再继续执行完成的,并且线程1的释放锁不是在notify后就立即释放而是该线程执行完毕后释放。
notify和notifyAll()
/** * 1、看notify()和notifyAll()的区别 * 2、观察notify()在所有线程都进入阻塞态前就执行的结果 */ public class WaitNotifyAll implements Runnable { private static final Object RESOURCE = new Object(); @Override public void run() { synchronized (RESOURCE) { System.out.println(Thread.currentThread().getName() + " get RESOURCE lock"); try { System.out.println(Thread.currentThread().getName()+" waits"); RESOURCE.wait(); System.out.println(Thread.currentThread().getName()+" 'reget RESOURCE lock"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new WaitNotifyAll()); Thread t2 = new Thread(new WaitNotifyAll()); Thread t3 = new Thread(new Runnable() { @Override public void run() { synchronized (RESOURCE){ RESOURCE.notifyAll(); // RESOURCE.notify(); System.out.println("Thread 3 notified"); } } }); t1.start(); t2.start(); //确保t1,t2都已经执行 Thread.sleep(200); t3.start(); } } 复制代码
使用notifyAll()会唤醒所有使用RESOURCE锁的线程
只释放当前monitor
/** * 证明wait只释放当前的那把锁 */ public class WaitNotifyReleaseOwnMonitor { private static final Object ResourceA = new Object(); private static final Object ResourceB = new Object(); public static void main(String[] args) { Thread t1 = new Thread(()->{ synchronized (ResourceA) { System.out.println("T1 get ResourceA lcok"); synchronized (ResourceB) { System.out.println("T1 get ResourceB lock"); try { System.out.println("T1 release ResourceA lock"); ResourceA.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } ); Thread t2 = new Thread(()->{ //让t1先运行起来,或得到A、B两把锁 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (ResourceA) { System.out.println("T2 get ResourceA lock"); System.out.println("T2 try to get ResourceB lock"); synchronized (ResourceB) { System.out.println("T2 get ResourceB lock"); } } }); t1.start(); t2.start(); } } 复制代码
从结果可以看出来,某个对象只释放当前对应的monitor锁,T2没有获得到ResourceB的锁。
1、不释放锁,包括 synchronized 和 lock ,这是与wait最大的不同点
/** * 考查sleep是否释放synchrd的monitor锁 */ public class SleepDontReleaseMonitor implements Runnable{ public static void main(String[] args) { SleepDontReleaseMonitor run = new SleepDontReleaseMonitor(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); t1.start(); t2.start(); } @Override public void run() { syn(); } private synchronized void syn() { System.out.println(Thread.currentThread().getName()+" starts"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" ends"); } } 复制代码
可以看出来,两个线程串行执行,sleep并不释放锁
对于lock锁
/** * 考查sleep是否释放lock锁(不会) */ public class SleepDontReleaseLock implements Runnable{ private static final Lock lock = new ReentrantLock(); @Override public void run() { lock.lock(); System.out.println(Thread.currentThread().getName()+" 获得了锁"); try { Thread.sleep(5000L); System.out.println(Thread.currentThread().getName()+" sleep结束,苏醒"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) { SleepDontReleaseMonitor run = new SleepDontReleaseMonitor(); new Thread(run).start(); new Thread(run).start(); } } 复制代码
结果和上一个是一致的。
2、sleep方法响应中断
(1) 抛出InterruptedException
(2) 清除中断状态
/** * 每过1秒钟输出当前时间,被中断,观察 * Thread.sleep() * TimeUnit.SECONDS.sleep() */ public class SleepInterrupted implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(new Date()); try { // TimeUnit.MINUTES.sleep(1); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("线程被中断了"); } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new SleepInterrupted()); t1.start(); Thread.sleep(3500); t1.interrupt(); Thread.sleep(3500); t1.interrupt(); } } 复制代码
1、由于有新的线程加入我们,所以我们要等其执行完再出发 尝试用main等待thread0、1执行完毕,注意是谁等谁(主线程等子线程)
普通用法
/** * 描述: 演示join,注意语句输出顺序,会变化。 */ public class joinNormal { static class WaitRunnable implements Runnable { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"执行完毕"); } } public static void main(String[] args) throws Exception{ Thread t1 = new Thread(new WaitRunnable()); Thread t2 = new Thread(new WaitRunnable()); t1.start(); t2.start(); System.out.println("开始等待子线程运行完毕"); t1.join(); t2.join(); System.out.println("所有线程都已执行完毕"); } } 复制代码
如果注释掉两行join,主线程和子线程是并行执行的,没有等待子线程先执行完毕.
2、主线程在等待子线程join的过程中,如果被中断,那么就不会再等待子线程执行完成了,而是catch到这个中断。
public class joinInterrupt { public static void main(String[] args) { Thread mainThread = Thread.currentThread(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { mainThread.interrupt(); Thread.sleep(5000); System.out.println("Thread1 finished."); } catch (InterruptedException e) { System.out.println("子线程中断"); } } }); thread1.start(); System.out.println("等待子线程运行完毕"); try { thread1.join(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+"主线程中断了"); //thread1.interrupt(); } System.out.println("子线程已运行完毕"); } } 复制代码
可以看出,主线程已经运行结束了,子线程才运行结束。这样就会导致主线程和子线程的不同步,如果要处理的话就应该使用 thread1.interrupt();
让子线程也停止下来。
3、在等待子线程join的过程中,主线程是什么状态(Waiting)
public class joinMainState { public static void main(String[] args) throws InterruptedException { Thread mainThread = Thread.currentThread(); Thread thread = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); //查看的是主线程的状态! System.out.println(mainThread.getState()); System.out.println("Thread-0运行结束"); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start(); System.out.println("等待子线程运行完毕"); thread.join(); System.out.println("子线程运行完毕"); } } 复制代码