您的位置:首页 > 移动开发 > Objective-C

Java并发07:Thread的基本方法(4)-Thread.sleep()、Object.wait()、notify()和notifyAll()

2018-03-12 15:08 781 查看
[超级链接:Java并发学习系列-绪论]

本章主要对Java中Thread类的基本方法进行学习。

1.序言

Thread类作为线程的基类,提供了一系列方法,主要有:

Thread.sleep(long):强制线程睡眠一段时间。

Thread.activeCount():获取当前程序中存活的线程数。

thread.start():启动一个线程。

Thread.currentThread():获取当前正在运行的线程。

thread.getThreadGroup():获取线程所在线程组。

thread.getName():获取线程的名字。

thread.getPriority():获取线程的优先级。

thread.setName(name):设置线程的名字。

thread.setPriority(priority):设置线程的优先级。

thread.isAlive():判断线程是否还存活着。

thread.isDaemon():判断线程是否是守护线程。

thread.setDaemon(true):将指定线程设置为守护线程。

thread.join():在当前线程中加入指定线程,使得这个指定线程等待当前线程,并在当前线程结束前结束。

thread.yield():使得当前线程退让出CPU资源,把CPU调度机会分配给同样线程优先级的线程。

thread.interrupt():使得指定线程中断阻塞状态,并将阻塞标志位置为true。

object.wai()、object.notify()、object.notifyAll():Object类提供的线程等待和线程唤醒方法。

为了便于阅读,将以上所有方法,放在5篇文章中进行学习。

本章主要学习绿色字体标记的方法,其他方法请参加其他章节。

2.Object.wait()与Thread.sleep()

先来看看sleep()的定义与注释:

/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
* ...
*/
public static native void sleep(long millis) throws InterruptedException;


说明:

sleep():属于Thread类的方法。

sleep():让当前正在运行的线程休眠指定毫秒的时间。

sleep():休眠的线程并不会失去任何的监视器(可以理解为成锁)

再来看看wait()方法的定义与注释:

/**
* Causes the current thread to wait until another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object.
* In other words, this method behaves exactly as if it simply
* performs the call {@code wait(0)}.
* <p>
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
* <p>
* ...
*/
public final void wait() throws InterruptedException {
wait(0);
}


说明:

wait():wait()是Object的方法。

wait():让当前线程等待,直到另一个线程调用了当前对象上的notify()或者notifyAll()方法。

wait():在调用时,会释放当前对象的监视器的所有权(可以理解成解锁)

调用wait()类的线程必须拥有这个对象的监视器(可以理解成锁)。

wait()的线程会一直等待,直到另一线程通知当前对象上的所有线程通过notify()唤醒单个线程或者通过notifyAll()唤醒全部线程。

wait()也可以只等待一定的时间就自动唤醒,方法是wait(long)。

sleep()wait()的区别和联系:

二者都是让线程暂停运行。

sleep()不会释放任何锁,wait()会释放对象上的锁。

sleep()正常恢复的方式只能是等待时间耗尽,wait()除了等待时间耗尽,还可以被其他线程唤醒(notify()和notifyAll)。

3.Object.notify()和Object.notifyAll()

先来看看这Object.notify()的定义和注释:

/**
* Wakes up a single thread that is waiting on this object's
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object's
* monitor by calling one of the {@code wait} methods.
* <p>
* ...
*/
public final native void notify();


说明:

notify():唤醒等待对象监视器的单个线程。

notify():如果等待对象监视器的有多个线程,则选取其中一个线程进行唤醒。

notify():选择唤醒哪个线程是任意的,由CPU自己决定。

再来看看这Object.notifyAll()的定义和注释:

/**
* Wakes up all threads that are waiting on this object's monitor. A
* thread waits on an object's monitor by calling one of the
* {@code wait} methods.
* <p>
* ...
*/
public final native void notifyAll();


说明:

notifyAll():唤醒等待对象监视器的所有线程。

很显然,notify和notifyAll一个是唤醒单个线程,一个是唤醒所有线程,前提是都在指定的对象监视器上。

4.实例代码与结果

4.1.实例场景

这是一个典型的生产者与消费者的示例。

程序中有两个类:厨房类和餐厅类。厨房负责炒菜(生产者),餐厅负责卖菜(消费者)。

厨房中只有一个厨子,他炒好一道菜之后,需要休息2秒钟。

厨房中有菜架子,能够存放炒好的菜,作为储备,以应对生意火爆的饭点时间。

菜架子上最多盛放6个盘子。所以,当菜架子上盛满6道菜之后,厨师就可以暂时休息了。

餐厅负责卖菜,大概需要花费1.5秒到2.5秒才能卖出一道菜。

餐厅会时刻查看菜架子上的菜品数量,当储备的菜品少于2盘时,就通知厨师该继续炒菜了,以免出现供不应求的情况。

在饭点时间,虽然厨师一直在炒菜,也可能因为卖菜卖的太快,导致菜架子上一盘菜都没有,这时餐厅只能耐心等待厨师炒菜。

4.2.实现思路

这是一个典型的线程等待与唤醒的问题。需要用到synchronized关键字、wait()以及notify()方法。

wait()以及notify()方法都需要锁定共同的对象,在这个场景中,这个共同的对象就是:菜架子(厨房通过菜架子盛放菜品,餐厅从菜架子获取菜品)。

当菜架子上盛满6道菜之后,厨师就可以暂时休息了—-这就是调用wait()的时机。

当储备的菜品少于2盘时,就通知厨师该继续炒菜了—-这就是调用notify()的时机。

4.3.实例代码

关于实例代码的其他说明:

将菜架子以队列Queue的形式实现,以FIFO(First in, First out,先进先出)。

代码:

/**
* <p>线程基本方法(sleep、wait、notify、notifyAll、synchronized)</p>
*
* @author hanchao 2018/3/11 14:14
**/
public class ThreadWaitDemo {
private static final Logger LOGGER = Logger.getLogger(ThreadWaitDemo.class);

//现有菜品
private static Queue<String> foodQueue = (Queue<String>) new LinkedList<String>();
//厨房的菜架能够存放菜品的最大值
private static int maxSize = 6;
//当厨房还剩几个菜时,继续炒菜
private static int minSize = 2;

/**
* <p>菜品工具类</p>
*
* @author hanchao 2018/3/11 15:03
**/
public static class Foods {
private static String[] foods = new String[]{"[鱼香肉丝]", "[水煮肉片]", "[地三鲜]", "[红烧肉]", "[干煸豆角]"};

/**
* <p>随机获取一个菜名</p>
*
* @author hanchao 2018/3/11 15:46
**/
public static String randomFood() {
return foods[RandomUtils.nextInt(0, foods.length)];
}
}

/**
* <p>厨房生产各种菜肴(wait、notify、synchronized)</p>
*
* @author hanchao 2018/3/11 14:21
**/
static class Kitchen extends Thread {
@Override
public void run() {
while (true) {
synchronized (foodQueue) {//加锁
//菜架满了,厨房不必再茶菜,等着前厅通着再炒菜
if (maxSize == foodQueue.size()) {
try {
LOGGER.info("厨房菜架满了,厨房不必再茶菜,等着前厅通着再炒菜,当前菜架:" + foodQueue.toString());
foodQueue.wait(111);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//炒一个菜
String food = Foods.randomFood();
foodQueue.add(food);
try {
LOGGER.info("厨房炒了一个:" + food + ",厨师歇息2分钟...当前菜架:" + foodQueue.toString());
//抄完一个菜,歇息1分钟
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}

/**
* <p>餐厅消费各种菜肴(wait、notify、synchronized)</p>
*
* @author hanchao 2018/3/11 14:54
**/
static class Restaurant extends Thread {
@Override
public void run() {
while (true) {
synchronized (foodQueue) {//加锁
//如果生意太好,菜品供不应求,只能等待厨房做菜...
if (0 == foodQueue.size()) {
try {
LOGGER.info("餐厅:生意太好,菜品供不应求,只能等待厨房做菜...当前菜架:" + foodQueue.toString());
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (foodQueue.size() > 0) {//如果有菜,则消费菜品
//当厨房的储备菜品所剩不多时,告诉厨师开始炒菜
if (foodQueue.size() <= minSize) {
foodQueue.notify();
LOGGER.info("餐厅:厨房的储备菜品所剩不多时,厨师们该继续炒菜了...");
}
//消费菜品
String food = foodQueue.poll();
try {
//随机一定时间吃掉一道菜
Thread.sleep(RandomUtils.nextInt(1500, 2500));
LOGGER.info("餐厅:刚刚消费了一道" + food + "...当前菜架:" + foodQueue.toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}

/**
* <p>线程基本方法(sleep、wait、notify、notifyAll、synchronized)</p>
*
* @author hanchao 2018/3/11 14:15
**/
public static void main(String[] args) throws InterruptedException {
//通过关键字synchronized和Object的方法wait()/notify()/notifyAll()实现线程等待与唤醒
//通过object.wait(),使得对象线程进行入等待唤醒状态,并是否对象上的锁
//通过object.notify()/object.notifyALL(),唤醒此对象上等待的线程,并获得对象上的锁
//wait()/notify()/notifyAll()必须在synchronized中使用
new Kitchen().start();
//先让厨房多炒几个菜
Thread.sleep(10000);
//餐厅开始消费
new Restaurant().start();
}
}


4.4.运行结果

运行结果:

2018-03-12 14:44:56 INFO  ThreadWaitDemo:65 - 厨房炒了一个:[水煮肉片],厨师歇息2分钟...当前菜架:[[水煮肉片]]
2018-03-12 14:44:58 INFO  ThreadWaitDemo:65 - 厨房炒了一个:[鱼香肉丝],厨师歇息2分钟...当前菜架:[[水煮肉片], [鱼香肉丝]]
2018-03-12 14:45:00 INFO  ThreadWaitDemo:65 - 厨房炒了一个:[鱼香肉丝],厨师歇息2分钟...当前菜架:[[水煮肉片], [鱼香肉丝], [鱼香肉丝]]
2018-03-12 14:45:02 INFO  ThreadWaitDemo:65 - 厨房炒了一个:[干煸豆角],厨师歇息2分钟...当前菜架:[[水煮肉片], [鱼香肉丝], [鱼香肉丝], [干煸豆角]]
2018-03-12 14:45:04 INFO  ThreadWaitDemo:65 - 厨房炒了一个:[干煸豆角],厨师歇息2分钟...当前菜架:[[水煮肉片], [鱼香肉丝], [鱼香肉丝], [干煸豆角], [干煸豆角]]
2018-03-12 14:45:08 INFO  ThreadWaitDemo:106 - 餐厅:刚刚消费了一道[水煮肉片]...当前菜架:[[鱼香肉丝], [鱼香肉丝], [干煸豆角], [干煸豆角]]
2018-03-12 14:45:10 INFO  ThreadWaitDemo:106 - 餐厅:刚刚消费了一道[鱼香肉丝]...当前菜架:[[鱼香肉丝], [干煸豆角], [干煸豆角]]
2018-03-12 14:45:11 INFO  ThreadWaitDemo:106 - 餐厅:刚刚消费了一道[鱼香肉丝]...当前菜架:[[干煸豆角], [干煸豆角]]
2018-03-12 14:45:11 INFO  ThreadWaitDemo:99 - 餐厅:厨房的储备菜品所剩不多时,厨师们该继续炒菜了...
2018-03-12 14:45:13 INFO  ThreadWaitDemo:106 - 餐厅:刚刚消费了一道[干煸豆角]...当前菜架:[[干煸豆角]]
2018-03-12 14:45:13 INFO  ThreadWaitDemo:99 - 餐厅:厨房的储备菜品所剩不多时,厨师们该继续炒菜了...
2018-03-12 14:45:15 INFO  ThreadWaitDemo:106 - 餐厅:刚刚消费了一道[干煸豆角]...当前菜架:[]
2018-03-12 14:45:15 INFO  ThreadWaitDemo:90 - 餐厅:生意太好,菜品供不应求,只能等待厨房做菜...当前菜架:[]
2018-03-12 14:45:17 INFO  ThreadWaitDemo:65 - 厨房炒了一个:[红烧肉],厨师歇息2分钟...当前菜架:[[红烧肉]]
2018-03-12 14:45:19 INFO  ThreadWaitDemo:65 - 厨房炒了一个:[鱼香肉丝],厨师歇息2分钟...当前菜架:[[红烧肉], [鱼香肉丝]]
2018-03-12 14:45:21 INFO  ThreadWaitDemo:65 - 厨房炒了一个:[干煸豆角],厨师歇息2分钟...当前菜架:[[红烧肉], [鱼香肉丝], [干煸豆角]]
2018-03-12 14:45:25 INFO  ThreadWaitDemo:106 - 餐厅:刚刚消费了一道[红烧肉]...当前菜架:[[鱼香肉丝], [干煸豆角]]
2018-03-12 14:45:25 INFO  ThreadWaitDemo:99 - 餐厅:厨房的储备菜品所剩不多时,厨师们该继续炒菜了...
2018-03-12 14:45:28 INFO  ThreadWaitDemo:106 - 餐厅:刚刚消费了一道[鱼香肉丝]...当前菜架:[[干煸豆角]]
2018-03-12 14:45:28 INFO  ThreadWaitDemo:99 - 餐厅:厨房的储备菜品所剩不多时,厨师们该继续炒菜了...
2018-03-12 14:45:30 INFO  ThreadWaitDemo:106 - 餐厅:刚刚消费了一道[干煸豆角]...当前菜架:[]
2018-03-12 14:45:30 INFO  ThreadWaitDemo:90 - 餐厅:生意太好,菜品供不应求,只能等待厨房做菜...当前菜架:[]
...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐