本章主要对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 final Queue<String> FOOD_QUEUE = (Queue<String>) new LinkedList<String>();
/**
* <p>菜品工具类</p>
**/
public static class Foods {
private static String[] foods = new String[]{"[鱼香肉丝]", "[水煮肉片]", "[地三鲜]", "[红烧肉]", "[干煸豆角]"};
/**
* <p>随机获取一个菜名</p>
*
* @author hanchao 2018/3/11 15:46
**/
static String randomFood() {
return foods[RandomUtils.nextInt(0, foods.length)];
}
}
/**
* <p>厨房生产各种菜肴(wait、notify、synchronized)</p>
**/
static class Kitchen extends Thread {
@Override
public void run() {
while (true) {
//加锁
synchronized (FOOD_QUEUE) {
//菜架满了,厨房不必再茶菜,等着前厅通着再炒菜
//厨房的菜架能够存放菜品的最大值
int maxSize = 6;
if (maxSize == FOOD_QUEUE.size()) {
try {
LOGGER.info("厨房菜架满了,厨房不必再茶菜,等着前厅通着再炒菜,当前菜架:" + FOOD_QUEUE.toString());
FOOD_QUEUE.wait(111);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//炒一个菜
String food = Foods.randomFood();
FOOD_QUEUE.add(food);
try {
LOGGER.info("厨房炒了一个:" + food + ",厨师歇息2分钟...当前菜架:" + FOOD_QUEUE.toString());
//抄完一个菜,歇息1分钟
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* <p>餐厅消费各种菜肴(wait、notify、synchronized)</p>
**/
static class Restaurant extends Thread {
@Override
public void run() {
while (true) {
//加锁
synchronized (FOOD_QUEUE) {
//如果生意太好,菜品供不应求,只能等待厨房做菜...
if (0 == FOOD_QUEUE.size()) {
try {
LOGGER.info("餐厅:生意太好,菜品供不应求,只能等待厨房做菜...当前菜架:" + FOOD_QUEUE.toString());
FOOD_QUEUE.wait(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (FOOD_QUEUE.size() > 0) {
//如果有菜,则消费菜品
//当厨房的储备菜品所剩不多时,告诉厨师开始炒菜
//当厨房还剩几个菜时,继续炒菜
int minSize = 2;
if (FOOD_QUEUE.size() <= minSize) {
FOOD_QUEUE.notify();
LOGGER.info("餐厅:厨房的储备菜品所剩不多时,厨师们该继续炒菜了...");
}
//消费菜品
String food = FOOD_QUEUE.poll();
try {
//随机一定时间吃掉一道菜
Thread.sleep(RandomUtils.nextInt(1500, 2500));
LOGGER.info("餐厅:刚刚消费了一道" + food + "...当前菜架:" + FOOD_QUEUE.toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* <p>线程基本方法(sleep、wait、notify、notifyAll、synchronized)</p>
**/
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 - 餐厅:生意太好,菜品供不应求,只能等待厨房做菜...当前菜架:[]
...