一:wait/notify详解
1.1:wait方法,立即释放锁
public class Demo {
private static Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
try {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+" :wait start");
lock.wait();
System.out.println(Thread.currentThread().getName()+" :wait end");
}
}catch (InterruptedException e){
e.printStackTrace();
}
},"高铁").start();
new Thread(() -> {
try {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+" :wait start");
lock.wait();
System.out.println(Thread.currentThread().getName()+" :wait end");
}
}catch (InterruptedException e){
e.printStackTrace();
}
},"飞机").start();
}
}
- 控制台
高铁 :wait start
飞机 :wait start
注: 程序还没停止!
分析:
假设wait()方法不释放锁,那么当高铁线程进入后,被wait()住了,那么飞机线程是不可能进去的,因为是同一把锁
而由控制台可以看出,就算是同一把锁,飞机线程也还是进去了,所以结论是wait()方法会立即释放锁!
1.2:sleep方法, 在指定的时间内不释放锁
注: 还是上面的例子,只不过将wait()
改成sleep()
进行演示
public class Demo {
private static Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
try {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+" :sleep start");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+" :sleep end");
}
}catch (InterruptedException e){
e.printStackTrace();
}
},"高铁").start();
new Thread(() -> {
try {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+" :sleep start");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+" :sleep end");
}
}catch (InterruptedException e){
e.printStackTrace();
}
},"飞机").start();
}
}
高铁 :sleep start ①
高铁 :sleep end ②
飞机 :sleep start ③
飞机 :sleep end ④
注:程序已经停止
分析:
由控制台可以看出,高铁线程执行完①后,一直等了5秒后,才去执行②,说明5秒期间高铁线程并没有去释放锁
三:wait(long timeout)
被其他线程对锁进行notify()通知唤醒或者时间到自动唤醒后,如果想向下运行,则需要重新持有锁!
如果没有锁,则一直等待,直到持有锁为止!
大白话:就是你被唤醒了,并不说明你可以马上往下运行,还是得去抢锁!抢到了才能往下运行!
1、业务类
public class MyService {
public void methodA(){
try {
synchronized (this){
System.out.println("wait begin: "+Thread.currentThread().getName()+" ,timer: "+System.currentTimeMillis());
this.wait(3000);
System.out.println("wait end: "+Thread.currentThread().getName()+" ,timer: "+System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
//同步方法的锁对象是this
public synchronized void methodB(){
try {
Thread.sleep(6000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
注:两个方法使用的是同一把锁!
2、线程类(两个)
ThreadA线程:执行业务类中的methodA方法
public class ThreadA extends Thread{
private MyService myService;
public ThreadA(MyService myService){
this.myService = myService;
}
@Override
public void run() {
myService.methodA();
}
}
ThreadB线程:执行业务类中的methodB方法
public class ThreadB extends Thread{
private MyService myService;
public ThreadB(MyService myService){
this.myService = myService;
}
@Override
public void run() {
myService.methodB();
}
}
3、测试类
public class Demo {
public static void main(String[] args) {
MyService myService = new MyService();
ThreadA[] myThreadAS = new ThreadA[10];
//0~1, 赋值并启动
IntStream.range(0,2).forEach(i -> {
myThreadAS[i] = new ThreadA(myService);
myThreadAS[i].start();
});
ThreadB threadB = new ThreadB(myService);
threadB.start();
}
}
4、控制台
wait begin: Thread-0 ,timer: 1596546932903
wait begin: Thread-1 ,timer: 1596546932903
wait end: Thread-1 ,timer: 1596546938917
wait end: Thread-0 ,timer: 1596546938917
5、分析
wait begin: Thread-0 ,timer: 1596546932903
wait begin: Thread-1 ,timer: 1596546932903
1.时间一模一样:第一个A线程先拿到锁,然后输出wait begin,执行完wait(3000)方法后,进入TIMED_WAITING状态,并释放锁,第二个A线程拿到锁之后步骤和上面一样,由控制台可以看出,上面执行完后甚至1毫秒都没花费掉,可见速度多快!!
2.B线程同时也启动了,但由于之前没抢到锁,所以没执行run中的任务,因为A线程和B线程抢的是同一把锁,
此时两个A线程都进入了TIMED_WAITING状态,那么B线程此时就可以轻而易举的抢到了锁,然后执行里面的Thread.sleep(6000),但该方法在指定的时间内并不会去释放锁,在线程B方法中等待3秒的同时,线程A中的wait(3000)也自动被唤醒了,但由于线程B中的sleep方法并不释放锁,而线程A就算被唤醒了,也不能立马往下执行,还是需要抢到锁往下执行,所以只能乖乖的等到线程B休眠6秒后,此时线程A才拿到锁,输出下面的wait end语句.
来自:虽然帅,但是菜的cxy