一、线程通讯
所谓线程通讯指的是在一个线程中的操作可以影响另一个线程
线程通讯的主要方法:wait()休眠线程 (唤醒一个线程notify)(唤醒全部线程notifyall)
wait() 使用时不和synchronized配合使用会出现监视器异常,如下:
public class Test29 {
public static void main(String[] args) {
Object lock = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
}
}
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
配合使用如下:
public class Test29 {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
//调用wait 方法之前必须先加锁
synchronized (lock) {
System.out.println("wait之前");
lock.wait();
System.out.println("wait之后");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
//注意一定要休眠一段时间,不然可能t1线程还没有进入休眠状态,主线程已经执行完了唤醒线程的操作
Thread.sleep(500);
System.out.println("主线程唤醒t1");
//在主线程中唤醒线程t1
synchronized (lock) {
lock.notify();
//lock.notifyAll();
}
}
}
wait之前
主线程唤醒t1
wait之后
这样的代码就实现了线程通讯:在主线程中通知子线程继续执行
注意事项:
1.wait方法在执行之前一定要加锁,也就是说wait在使用的时候要配合synchronized(释放锁)一起使用
2.wait和notify在配合synchronized使用时一定要使用同一把锁
3.wait和notify在配合使用时一定要操作同一把锁
★wait()不管是否传参,都会释放锁对象(因为synchronized会自动释放锁对象)
★wait()在不传递任何参数的情况下会进入waiting(无限等待)状态(不传递参数默认调用wait(0)方法)
★当传递一个大于0的整数时,会进入timed_waiting(超时等待状态)状态(传递0进入waiting状态,传递负数就会报错)
二、notify和notifyall唤醒线程
同样,notify和notifyall使用的时候也必须配合synchrozied一起使用,不然会报错。
notify是随机唤醒一个线程,而notifyall是唤醒所有的线程
public class Test30 {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("t1 wait 之前");
lock.wait();
System.out.println("t2 wait 之后");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("t2 wait 之前");
lock.wait();
System.out.println("t2 wait 之后");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t2");
t2.start();
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("t3 wait 之前");
lock.wait();
System.out.println("t3 wait 之后");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t3");
t3.start();
Thread.sleep(500);
System.out.println("主线程调用唤醒方法");
// 在主线程中唤醒线程 t1
//这样的唤醒方式是随机的,不确定唤醒的是哪个线程
synchronized (lock) {
lock.notify();
// lock.notifyAll();
}
}
}
t1 wait 之前
t2 wait 之前
t3 wait 之前
主线程调用唤醒方法
t2 wait 之后
Process finished with exit code -1
wait和notify/notifyall的问题:不能唤醒指定的线程
唤醒指定线程的方法:lockSupport(park()、unpark()) (如果不进行唤醒,会进入waiting状态)
import java.util.concurrent.locks.LockSupport;
public class Test31 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//让线程进行休眠
LockSupport.park();
System.out.println("唤醒t1");
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//让线程进行休眠
LockSupport.park();
System.out.println("唤醒t2");
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
//让线程进行休眠
LockSupport.park();
System.out.println("唤醒t3");
}
},"t3");
t1.start();
t2.start();
t3.start();
//唤醒指定线程
LockSupport.unpark(t3);
}
}
唤醒t3
LockSupport虽然不会报Interrupt的异常,但依旧可以监听到线程终止的命令。
import java.util.concurrent.locks.LockSupport;
public class Test31 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("park 之前 Interrupt 状态:" +
Thread.currentThread().isInterrupted());
// 线程进入休眠
LockSupport.park();
System.out.println("park 之后 Interrupt 状态:" +
Thread.currentThread().isInterrupted());
}
}, "t1");
// 启动线程
t1.start();
Thread.sleep(100);
// 中止线程
t1.interrupt();
// 唤醒线程 t1
LockSupport.unpark(t1);
}
}
park 之前 Interrupt 状态:false
park 之后 Interrupt 状态:true
面试题1:wait()和sleep()区别:(面试必考)
相同点:
1.都是让线程进入休眠状态
2.wait()和sleep()在执行过程中都可以得到终止异常的通知
3.wait()是用于线程之间通信的,sleep()是让线程阻塞一段时间
不同点:
1.wait()使用必须配合synchronized使用,而sleep不需要
2.wait()执行时会释放锁,sleep()执行不会释放锁
3.wait()是Object的方法,而sleep()是Thread线程方法
4.默认情况下wait(不传递任何参数或者传递的参数为0)的情况下会进入waiting状态,而sleep会进入timed_waiting状态
5.使用wait时可以主动唤醒线程,而使用sleep时不能主动唤醒线程
面试题2:sleep(0)和wait(0)有什么区别
1.sleep(0)表示过0毫秒之后继续执行,而wait(0)表示一直休眠
2.sleep(0)表示重新触发一次CPU竞争,wait(0)不会触发CPU竞争
面试题3:为什么wait会释放锁,而sleep不会释放锁
答:sleep必须要传递一个最大等待时间的,也就是说sleep是可控的(时间层面来讲),而wait是可以不传递参数的(不可控),从设计层面来讲,如果让wait这个没有超时等待时间的机制不释放锁的话,那么线程可能一直阻塞,而sleep就不存在这个问题。
面试题4:为什么sleep是Object的方法,而wait是Thread的方法?
答:wait需要操作锁,而锁是属于对象级别的(所有的锁都是放在对象头当中的),他不是线程级别的,一个线程中可以有多把锁,为了灵活起见,所以就将wait放在Object当中