目录
- sleep和wait的区别?
sleep和wait的区别?
1. sleep方法(休眠)
1. 源码分析
class Thread implements Runnable {
public static native void sleep(long millis) throws InterruptedException;
}
sleep方法是Thread类的实例方法,且这个方法被native修饰,是一个本地库接口
2. 作用
让线程进入阻塞状态,并主动让出CPU,但还会占有锁
3. 题外话:sleep之后线程会释放锁吗
不会,sleep方法只对CPU操作,并不会去操作对象的锁(tips:对象锁的状态是保存在对象头的,这个在【八股文】JVM篇有讲
4. sleep的使用场景
高优先级线程因为一些原因(自身操作比较耗时、需要低优先级线程的数据),需要让低优先级线程先执行,可以自己调用sleep操作,让出CPU
2. wait方法(等待)
1. 源码分析
public class Object {
public final void wait() throws InterruptedException {
wait(0);
}
public final native void wait(long timeout) throws InterruptedException;
}
wait方法是Object对象类的实例方法,是对象的方法,其本质也是用native修饰的,是一个本地库方法
1. 作用
自己进入等待池,暂时(暂时体现在哪里?和sleep区别?)让出CPU的使用权,也会释放同步锁
2. 使用场景
- 生产者-消费者模式
3. notify
1. notify的作用
- 让等待池中的线程转移到锁池中(锁池中的线程会去积极地抢锁)
- notify方法不释放锁,要等方法执行完毕之后才会释放锁
2. 为什么要使用notify、notifyAll?
因为要提醒其他线程,从等待池到锁池
例如:
线程A调用wait方法之后,会进入等待池,线程A会把这个锁释放,供其他线程抢,线程B抢到这个锁,并跑完程序,想要释放这个锁给线程A用,但是线程A还在等待池中,这时候就必须要使用notify或notifyAll方法,把线程A叫到锁池里
3. 应用场景
- 生产者-消费者模式
4. 如何使用wait和notifyAll实现生产者-消费者模式
1. 注意事项
- 使用wait和notify可以实现线程间的通信(多个线程可以使用这些方法相互通信)
- 必须获得锁才能使用这些方法,如synchronized或synchronized块里使用
- 用while代替if做条件判断
- 用notifyAll代替notify
1. 题外话:为什么wait和notify方法要在synchronized方法里使用?
synchronized的语义底层是通过一个monitor的对象来完成,其实wait、notify等方法也依赖于monitor对象,否则会抛出java.lang.IllegalMonitorStateException的异常(不合法的监视器状态)
2. 实现
1. 生产者
public class Producer implements Runnable {
private final List<Integer> container;
public static final int MAX_CAPACITY = 5;
public Producer(List<Integer> container) {
this.container = container;
}
private void produce() throws InterruptedException {
synchronized (container) {
while (container.size() == MAX_CAPACITY) {
System.out.println("容器已满,暂停生产");
container.wait();
}
int p = RandomUtils.nextInt();
System.out.println("生产产品:" + p);
container.add(p);
container.notifyAll();
}
}
@Override
public void run() {
while (true) {
try {
produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
1. 消费者
public class Consumer implements Runnable{
private List<Integer> container;
public Consumer(List<Integer> container) {
this.container = container;
}
private void consume() throws InterruptedException {
synchronized (container){
while (container.isEmpty()){
System.out.println("容器已空,暂停消费");
container.wait();
}
Integer p = container.remove(0);
TimeUnit.MILLISECONDS.sleep(1000);
System.out.println("消费产品:" + p);
container.notifyAll();
}
}
@Override
public void run() {
while (true){
try {
consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3. 生产者消费者测试
public class ProducerConsumerTest {
public static void main(String[] args) {
List<Integer> container = new ArrayList<>();
Thread producer = new Thread(new Producer(container));
Thread consumer = new Thread(new Consumer(container));
producer.start();
consumer.start();
}
}
4. 控制台现象
Connected to the target VM, address: '127.0.0.1:54861', transport: 'socket'
容器已空,暂停消费
生产产品:1539478251
生产产品:2055287902
生产产品:244175613
生产产品:836101091
生产产品:346467918
容器已满,暂停生产
消费产品:1539478251
消费产品:2055287902
消费产品:244175613
消费产品:836101091
消费产品:346467918
容器已空,暂停消费
生产产品:2056187818
生产产品:1148572189
生产产品:1951629848
生产产品:767221295
生产产品:2009198874
容器已满,暂停生产
1. 控制台现象分析
消费者发现容器为空,调用wait方法直接去等待池,生产者开始生产数据,生产者满了将调用wait,并notifyAll其他线程,消费者被唤醒,开始消费数据。。。如此往复执行。。
2. 问:为什么会一直循环下去?start启动一个线程之后,不会关闭吗?
如果代码运行完毕的话,是会关闭线程的,一直循环说明进入了while,编不下去了。。TODO搞不清,不太理解
5. 为什么wait方法可以释放对象的锁,而sleep不行?
- wait方法是Object的方法(对象的方法),那就代表着wait方法可以控制对象相关的一些属性,比如对象头中的一些属性(如对象锁的状态、持有锁的线程名),所以wait方法可以释放对象的锁
- sleep方法是Thread的方法,不能控制对象相关的操作,也就不能控制锁的释放了
6. sleep线程是怎么让出CPU的?原理是什么
sleep方法是native修饰的,代表是本地库接口,原理是其他语言实现的,我也不清楚