JAVA多线程的sleep(),wait(),notify(),notifyAll()方法学习心得体会
本文参考https://www.cnblogs.com/diegodu/p/7866073.html
sleep()方法
sleep是指让当前正在运行的线程休眠一段指定的时间,休眠的过程以及休眠结束继续运行都由当前线程自己控制。比如,我要吃一个苹果的流程是拿苹果-->洗苹果-->吃苹果。在我拿起苹果之后我突然想休息一会,在我休息了分钟之后,我再继续去执行洗苹果的动作,整个的动作执行由我这个线程控制。
wait()方法
wait也是让当前线程暂停(阻塞)一段时间,这个方法是由某个确定的对象来调用的。当这个对象在某个线程李说“暂停”,也就是this.wait();当前线程也就进入阻塞状态。就好比一个人监督着我要吃一个苹果的流程(拿苹果-->洗苹果-->吃苹果),在我洗完苹果后,他突然说暂停,这时我就进入暂停(阻塞)状态。必须等待他说继续也就是this.notify(),或者this.notifuAll()。然后我继续进行吃苹果的动作。
小结
其实两者都可以让线程暂停一段时间,但是本质的区别是一个线程的运行状态控制,一个是线程之间的通讯的问题
在java.lang.Thread类中,提供了sleep(),
而java.lang.Object类中提供了wait(), notify()和notifyAll()方法来操作线程
sleep()可以将一个线程睡眠,参数可以指定一个时间。
而wait()可以将一个线程挂起,直到超时或者该线程被唤醒。
wait有两种形式wait()和wait(milliseconds).
sleep和wait的区别有:
1,这两个方法来自不同的类分别是Thread和Object
2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
notify()和notifyAll()
这两个方法都是唤醒想成,最大区别是notify()是唤醒线程池中最先(最先两个字是重点)加入的一个线程,而notifyAll()是唤醒线程池中所有的线程。
生产消费模式举例
创建苹果对象:
public class Apple {
int id;
public Apple(int id){
this.id=id;
}
public String toString(){
return "apple:"+id;
}
}
生产消耗控制者:
public class AppleBox {
int index = 0;
Apple[] apples = new Apple[5];
//生产苹果
public synchronized void productApple(Apple apple) {
while(index>=apples.length){
try{
this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
this.notify();
apples[index]=apple;
System.out.println(Thread.currentThread().getName()+"生产编号为:"+apple.id+"的苹果");
index++;
}
//消耗苹果
public synchronized Apple eatApple(){
while (index<=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
index--;
System.out.println(Thread.currentThread().getName()+"消耗编号为:"+apples[index].id+"的苹果");
return apples[index];
}
}
生产者:
import java.util.Random;
public class Producer implements Runnable {
private AppleBox box;
public Producer(AppleBox box) {
this.box = box;
}
@Override
public void run() {
Random rd = new Random();
for (int i = 0; i < 20 ; i++) {
Apple apple = new Apple(i+1);
box.productApple(apple);
System.out.println("生产了:"+apple.id+"苹果");
try{
Thread.sleep(rd.nextInt(500));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者:
import java.util.Random;
public class Customer implements Runnable {
private AppleBox box;
public Customer(AppleBox box) {
this.box = box;
}
@Override
public void run() {
Random rd = new Random();
for (int i = 0; i < 20; i++) {
Apple apple = box.eatApple();
System.out.println("消耗了:"+apple.id+"苹果");
try{
Thread.sleep(rd.nextInt(500));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试方法:
public class Test {
public static void main(String[] args) {
AppleBox box = new AppleBox();
Producer producer = new Producer(box);
Customer customer = new Customer(box);
Customer customer1 = new Customer(box);
new Thread(producer).start();
new Thread(customer).start();
new Thread(customer1).start();
}
}
在控制方法中使用notify()唤醒方法,执行结果为:
Thread-0生产编号为:1的苹果
生产了:1苹果
Thread-1消耗编号为:1的苹果
消耗了:1苹果
Thread-0生产编号为:2的苹果
生产了:2苹果
Thread-2消耗编号为:2的苹果
消耗了:2苹果
Thread-0生产编号为:3的苹果
Thread-1消耗编号为:3的苹果
消耗了:3苹果
生产了:3苹果
Thread-0生产编号为:4的苹果
生产了:4苹果
Thread-2消耗编号为:4的苹果
消耗了:4苹果
Thread-0生产编号为:5的苹果
生产了:5苹果
Thread-1消耗编号为:5的苹果
消耗了:5苹果
Thread-0生产编号为:6的苹果
Thread-2消耗编号为:6的苹果
消耗了:6苹果
生产了:6苹果
Thread-0生产编号为:7的苹果
生产了:7苹果
Thread-1消耗编号为:7的苹果
消耗了:7苹果
Thread-0生产编号为:8的苹果
生产了:8苹果
Thread-2消耗编号为:8的苹果
消耗了:8苹果
Thread-0生产编号为:9的苹果
生产了:9苹果
Thread-1消耗编号为:9的苹果
消耗了:9苹果
Thread-0生产编号为:10的苹果
Thread-1消耗编号为:10的苹果
消耗了:10苹果
生产了:10苹果
Thread-0生产编号为:11的苹果
生产了:11苹果
Thread-2消耗编号为:11的苹果
消耗了:11苹果
Thread-0生产编号为:12的苹果
生产了:12苹果
Thread-1消耗编号为:12的苹果
消耗了:12苹果
Thread-0生产编号为:13的苹果
生产了:13苹果
Thread-2消耗编号为:13的苹果
消耗了:13苹果
Thread-0生产编号为:14的苹果
生产了:14苹果
Thread-2消耗编号为:14的苹果
消耗了:14苹果
Thread-0生产编号为:15的苹果
生产了:15苹果
Thread-1消耗编号为:15的苹果
消耗了:15苹果
Thread-0生产编号为:16的苹果
生产了:16苹果
Thread-2消耗编号为:16的苹果
消耗了:16苹果
Thread-0生产编号为:17的苹果
生产了:17苹果
Thread-1消耗编号为:17的苹果
消耗了:17苹果
Thread-0生产编号为:18的苹果
Thread-2消耗编号为:18的苹果
消耗了:18苹果
生产了:18苹果
Thread-0生产编号为:19的苹果
生产了:19苹果
Thread-2消耗编号为:19的苹果
消耗了:19苹果
Thread-0生产编号为:20的苹果
生产了:20苹果
Thread-1消耗编号为:20的苹果
消耗了:20苹果
当使用notifyAll()唤醒方法时,执行结果为:
Thread-0生产编号为:1的苹果
生产了:1苹果
Thread-1消耗编号为:1的苹果
消耗了:1苹果
Thread-0生产编号为:2的苹果
生产了:2苹果
Thread-1消耗编号为:2的苹果
消耗了:2苹果
Thread-0生产编号为:3的苹果
生产了:3苹果
Thread-1消耗编号为:3的苹果
消耗了:3苹果
Thread-0生产编号为:4的苹果
生产了:4苹果
Thread-2消耗编号为:4的苹果
消耗了:4苹果
Thread-0生产编号为:5的苹果
生产了:5苹果
Thread-2消耗编号为:5的苹果
消耗了:5苹果
Thread-0生产编号为:6的苹果
生产了:6苹果
Thread-1消耗编号为:6的苹果
消耗了:6苹果
Thread-0生产编号为:7的苹果
生产了:7苹果
Thread-2消耗编号为:7的苹果
消耗了:7苹果
Thread-0生产编号为:8的苹果
Thread-2消耗编号为:8的苹果
消耗了:8苹果
生产了:8苹果
Thread-0生产编号为:9的苹果
生产了:9苹果
Thread-2消耗编号为:9的苹果
消耗了:9苹果
Thread-0生产编号为:10的苹果
生产了:10苹果
Thread-2消耗编号为:10的苹果
消耗了:10苹果
Thread-0生产编号为:11的苹果
生产了:11苹果
Thread-1消耗编号为:11的苹果
消耗了:11苹果
Thread-0生产编号为:12的苹果
生产了:12苹果
Thread-2消耗编号为:12的苹果
消耗了:12苹果
Thread-0生产编号为:13的苹果
生产了:13苹果
Thread-1消耗编号为:13的苹果
消耗了:13苹果
Thread-0生产编号为:14的苹果
生产了:14苹果
Thread-2消耗编号为:14的苹果
消耗了:14苹果
Thread-0生产编号为:15的苹果
Thread-2消耗编号为:15的苹果
消耗了:15苹果
生产了:15苹果
Thread-0生产编号为:16的苹果
Thread-2消耗编号为:16的苹果
消耗了:16苹果
生产了:16苹果
Thread-0生产编号为:17的苹果
生产了:17苹果
Thread-2消耗编号为:17的苹果
消耗了:17苹果
Thread-0生产编号为:18的苹果
生产了:18苹果
Thread-1消耗编号为:18的苹果
消耗了:18苹果
Thread-0生产编号为:19的苹果
生产了:19苹果
Thread-2消耗编号为:19的苹果
消耗了:19苹果
Thread-0生产编号为:20的苹果
Thread-1消耗编号为:20的苹果
消耗了:20苹果
生产了:20苹果
可以看出第一次打印结果,消费者1和消费者2是轮流出现,这是由于两者被轮流唤醒导致;第二次打印结果,消费者1和消费者2是随机出现。这是由于两个线程同时被唤醒,然后争夺CPU资源所导致的。由此可以很明显的看出notify()和notifyAll()的区别。
后记:第一次写这种博客,有所不足请在评论中指出。谢谢。