一、Object类中的wait、notify、notifyAll
1、作用、用法
- wait、notify基本使用
/** * 展示wait和notify的基本用法 * * @author wjh * @date 2020-03-05 10:45 */ 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) { // 线程2唤醒 object.notify(); 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(100); thread2.start(); } }
运行结果:
notify、notifyAll使用
/** * 3个线程,线程1,线程2首先被阻塞,线程3唤醒它们。分别使用notify和notifyAll * * @author wjh * @date 2020-03-05 11:05 */ public class WaitNotifyAll implements Runnable { private static Object resourceA = new Object(); @Override public void run() { synchronized (resourceA) { System.out.println("线程" + Thread.currentThread().getName() + "获取到resourceA锁"); try { System.out.println("线程" + Thread.currentThread().getName() + "等待下次执行"); // 释放掉锁 resourceA.wait(); System.out.println("线程" + Thread.currentThread().getName() + "工作结束"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { WaitNotifyAll waitNotifyAll = new WaitNotifyAll(); Thread threadA = new Thread(waitNotifyAll); Thread threadB = new Thread(waitNotifyAll); Thread threadC = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { resourceA.notifyAll(); System.out.println("线程" + Thread.currentThread().getName() + "调用了notifyAll方法"); } } }); threadA.start(); threadB.start(); Thread.sleep(200); threadC.start(); } }
运行结果:
如果是使用notify,只能唤醒一个线程,剩下的没人唤醒程序无法结束运行
wait只释放当前的锁证明
/** * 证明wait只释放当前的那把锁 * * @author wjh * @date 2020-03-05 11:18 */ public class WaitNotifyReleaseOwnMonitor { private static volatile Object resourceA = new Object(); private static volatile Object resourceB = new Object(); public static void main(String[] args) { Thread threadA = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { System.out.println("线程" + Thread.currentThread().getName() + "获取到resourceA"); synchronized (resourceB) { System.out.println("线程" + Thread.currentThread().getName() + "获取到resourceB"); try { // 释放resourceA resourceA.wait(); System.out.println("线程" + Thread.currentThread().getName() + "释放resourceA"); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (resourceA) { System.out.println("线程" + Thread.currentThread().getName() + "获取到resourceA"); System.out.println("线程" + Thread.currentThread().getName() + "尝试获取resourceB"); synchronized (resourceB) { System.out.println("线程" + Thread.currentThread().getName() + "获取到resourceB"); } } } }); threadA.start(); threadB.start(); } }
运行结果:
线程A首先获取到了A锁和B锁,然后释放了A锁但是还持有B锁,然后线程B获取到A锁,等待获取B锁,而A没有唤醒不能释放B锁,程序陷入死锁
2、特点、性质
- 必须先拥有monitor(Java中synchronized)
- notify只能唤醒其中一个线程
- 都属于Object类,所以每一个类有拥有这些方法
- 类似功能的Condition
- 同时拥有多个锁,wait只会释放当前对象这把锁
二、Thread类中重要的方法
1、sleep
作用:能让线程在预期的时间执行,其他时候不占用CPU资源
特点:sleep不会释放锁(包括synchronized 和 lock)、sleep方法响应中断
演示:
1、不释放synchronized锁
/** * 演示线程sleep不释放synchronized的monitor,等sleep时间结束后,正常结束任务才会释放锁 * * @author wjh * @date 2020-03-05 13:03 */ public class SleepDontReleaseMonitor implements Runnable { public static void main(String[] args) { SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor(); Thread threadA = new Thread(sleepDontReleaseMonitor, "A"); Thread threadB = new Thread(sleepDontReleaseMonitor, "B"); threadA.start(); threadB.start(); } @Override public void run() { syn(); } private synchronized void syn() { System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "退出同步代码块"); } }
结果:
- 2、不释放lock
/** * 演示线程sleep不释放synchronized的monitor,等sleep时间结束后,正常结束任务才会释放锁 * * @author wjh * @date 2020-03-05 13:03 */ public class SleepDontReleaseLock implements Runnable { public static void main(String[] args) { SleepDontReleaseLock sleepDontReleaseLock = new SleepDontReleaseLock(); Thread threadA = new Thread(sleepDontReleaseLock, "A"); Thread threadB = new Thread(sleepDontReleaseLock, "B"); threadA.start(); threadB.start(); } private static final Lock lock = new ReentrantLock(); @Override public void run() { lock.lock(); System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁"); try { Thread.sleep(1000); System.out.println("线程" + Thread.currentThread().getName() + "sleep结束"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
结果:
- 3、响应中断
/** * @author wjh * @date 2020-03-05 13:17 */ public class SleepInterrupted implements Runnable { @Override public void run() { try { for (int i = 0; i < 10; i++) { System.out.println(new Date()); TimeUnit.SECONDS.sleep(1); } } catch (InterruptedException e) { System.out.println("线程被中断了"); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new SleepInterrupted()); thread.start(); TimeUnit.SECONDS.sleep(5); thread.interrupt(); } }
结果:
sleep与wait/notify的异同:
- 相同
1、阻塞
2、响应中断
- 不同
1、wait/notify需要在同步代码块中,sleep不需要
2、wait能释放锁,sleep不能
3、所属类不同,wait/notify属于Object类,sleep属于Thread类
4、时间的指定,如果wait不指定时间就需要别人唤醒
2、join
作用:使线程等待调用线程执行完毕
演示:
- 1、基本用法
/** * @author wjh * @date 2020-03-05 13:35 */ public class join { public static void main(String[] args) throws InterruptedException { Thread thread1 = 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() + "执行完毕"); } }); thread1.start(); thread2.start(); System.out.println("开始等待子线程运行完毕"); thread1.join(); thread2.join(); System.out.println("所有子线程运行完毕"); } }
结果:
- 2、演示中断
/** * @author wjh * @date 2020-03-05 13:42 */ 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("线程执行完毕"); } catch (InterruptedException e) { System.out.println("子线程中断"); } } }); thread.start(); System.out.println("等待子线程执行完毕"); try { thread.join(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "线程被中断"); thread.interrupt(); } System.out.println("子线程执行完毕"); } }
结果:
- 3、join的线程状态
/** * @author wjh * @date 2020-03-05 13:50 */ public class JoinThreadState { 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.getName() + "线程状态为:" + mainThread.getState()); System.out.println("子线程运行结束"); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start(); System.out.println("等待子线程运行完毕"); thread.join(); System.out.println("子线程运行完毕"); } }
结果:
join的等价代码 :也可以实现join的效果
synchronized (thread) { thread.wait(); }
3、yield
作用:释放CPU时间片
定位:JVM不保证遵循