前言:
- sleep(1000) 是 Thread 中的方法,参数是sleep多少毫秒。sleep() 的作用是将当前线程暂停一段时间,但这期间不会释放锁
- wait、notify、notifyAll 是 Object 中的方法,可以作用于任何对象,用于控制线程的状态,通常配合 synchronized 代码块使用
Sleep():
- sleep 的作用是使当前线程睡眠指定的时间,放弃对CPU的占用,但是不会放弃对线程以获取到的锁,例:
public class SleepTest extends Thread{
// 锁
public static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
SleepTest sleepTest = new SleepTest();
sleepTest.setName("test1");
sleepTest.start();
SleepTest sleepTest2 = new SleepTest();
sleepTest2.setName("test2");
sleepTest2.start();
}
@Override
public void run() {
System.out.println("ThreadName :" + Thread.currentThread().getName() + " start" );
synchronized (lock){
for (int i =0 ; i<5; i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadName :" + Thread.currentThread().getName() + "; i == " + i);
}
}
}
}
打印结果:
ThreadName :test2 start
ThreadName :test1 start
ThreadName :test2; i == 0
ThreadName :test2; i == 1
ThreadName :test2; i == 2
ThreadName :test2; i == 3
ThreadName :test2; i == 4
ThreadName :test1; i == 0
ThreadName :test1; i == 1
ThreadName :test1; i == 2
ThreadName :test1; i == 3
ThreadName :test1; i == 4
在main方法中我们,我们创建了两个线程,它们共同竞争同一把锁 lock ,根据打印结果可以看出,线程1 一直等到线程2 执行结束之后才执行 synchronized 中代码块, sleep(1000) 方法在线程2 中并没有释放 锁lock ,直至执行结束
等待池、锁池简介:
了解这个wait、notify、notifyAll三个方法之前,需要先了解两个概念:等待池、锁池;
- 等待池:当对象锁【lock】 调用 wait 方法时, 持有该对象锁的线程会进入到等待池中,并且释放当前对象锁,线程进入到被动状态,需要notify notifyAll 将其唤醒。
- 锁池: 假设某对象锁当前某线程获取,其他线程想要持有该对象锁时,需要进入到锁池中,等待对象锁被释放再去竞争锁,线程进入到主动状态,等待对象锁空闲。
wait、notify、notifyAll简介
- wait、notify、notifyAll 需要配合 synchronized (lock) 来使用,因为对对象锁的进行操作之前需要,对其先进行监听操作。
- wait() : 会将当前线程进入休眠状态,并且释放当前获取到的对象锁,并将当前线程放入到等待池中,等待notify、notifyAll 将其唤醒并进入锁池中竞争对象锁,
- notify 从等待池中随机选择【和优先级也有关】唤醒其中一个线程,进入到锁池中等待对象锁,并执行,其他等待池中线程继续等待。
- notifyAll 将等待池中所有的线程全部唤醒到锁池中,然后去竞争对象锁,
例 :
public class WaitTest extends Thread{
// 锁
public static final Object lock = new Object();
public static final Object lock1 = new Object();
public static void main(String[] args) throws InterruptedException {
// 创建线程一
WaitTest sleepTest = new WaitTest();
sleepTest.setName("test1");
sleepTest.start();
// 创建线程二
WaitTest sleepTest2 = new WaitTest();
sleepTest2.setName("test2");
sleepTest2.start();
// sleep 保证线程一、二调用 wait 进入到等待池中
Thread.sleep(1000);
synchronized (lock){
// 随机唤醒一个线程进入到锁池中等待对象锁
System.out.println("lock.notify");
lock.notify();
}
}
@Override
public void run() {
synchronized (lock){
try {
System.out.println("Thread-Name :" + Thread.currentThread().getName() + " wait ");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i =0 ; i< 5; i++){
System.out.println("Thread-Name :" + Thread.currentThread().getName() + "; i == " + i);
}
}
}
}
打印结果:
Thread-Name :test2 wait
Thread-Name :test1 wait
lock.notify
Thread-Name :test2; i == 0
Thread-Name :test2; i == 1
Thread-Name :test2; i == 2
Thread-Name :test2; i == 3
Thread-Name :test2; i == 4
主线程sleep(1000) 期间,线程一二都通过 wait 进入到等待池中,在sleep() 之后,调用lock.notify(),将线程二唤醒进入到锁池中,线程一继续等待被唤醒。
- lock.notify(); 换成 lock.notifyAll(); 之后打印结果:
Thread-Name :test1 wait
Thread-Name :test2 wait
lock.notifyAll
Thread-Name :test2; i == 0
Thread-Name :test2; i == 1
Thread-Name :test2; i == 2
Thread-Name :test2; i == 3
Thread-Name :test2; i == 4
Thread-Name :test1; i == 0
Thread-Name :test1; i == 1
Thread-Name :test1; i == 2
Thread-Name :test1; i == 3
Thread-Name :test1; i == 4
根据结果可以看出,线程一二全部被唤醒,等待 lock 锁之后全部执行完毕。