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("子线程运行完毕");
}
}
复制代码