java多线程:线程状态与线程等待唤醒机制的理解
程序员文章站
2022-05-04 18:09:36
...
写在前面
2020年疫情以来,坚持在csdn博客写文章已经有两个多月了,刚开始动笔的原因,主要就是为了把一些知识点及工作中碰到的问题,通过博客的方式记录下来,但随着时间的加长,对于写博客也有了一些新的认识,今天借这个机会,把个人想法记录在案:
刚开始的想法
- 刚开始写的时候担心自己坚持不下来
- 动笔了感觉没什么写,内容不充实
- 在互联网上写博客,总是要被人看的,怕被人笑话
- …
在内心忐忑之间,还是坐下来写了,反正写的都是技术文:从书上学到的,工作中碰到的,确有其事;文章中的样例代码,都是自己手把手写的且通过调试运行,贵在真实。
现在的心境
不以成为职业作家为目标的话,写文章其实没那么难。某种程度上,它就跟我们平时说话、唱歌一样,只是一种沟通交流的手段,源自我们与生俱来的表达欲望。
慢慢地,写博客成了近几个月的习惯,几天不码字感觉少了点什么:
- 把技术通过文字的形式写下来更容易理清逻辑,加深认知;
- 把知识点通过系列文章分段的方式写下来,是对思维的刻意训练;
- 坚持写博客,整理技术素材,能做到温故而知新;
- 通过互联网的传播,通过网友的点评,更加能鞭策作者。
- …
废话到此为止,接下来进入正题。
线程状态概述
java线程在运行的生命周期有6种不同的状态,在给定的一个时刻,线程只能处于其中的一个状态。
状态 | 说明 |
---|---|
NEW | 新建状态又称为初始状态,线程刚被创建,但没调用start()开始运行 |
RUNNABLE | 运行状态,java线程将操作系统中就绪和运行状态统称为”运行中“状态 |
BLOCKED | 阻塞状态,线程被”锁“阻塞时的状态 |
WAITING | 等待状态又称为永久等待状态,处于永久等待状态的线程需要被其他线程唤醒或中断 |
TIMED_WAITING | 休眠等待状态又称为计时等待状态,处于计时等待状态的线程休眠结束后自行唤醒 |
TERMINATED | 终止状态,线程运行结束 |
线程的等待与唤醒
- 创建一个全局的锁对象objectLock;
- 创建一个线程全局的运行标志flag;
- 运行线程0,并通过objectLock进行无限等待状态;
- 运行线程1,并通过objectLock唤醒该锁对象上的等待线程,并将运行标志flag置为结束;
- 线程0得到唤醒通知后,继续运行,并根据结束标志的状态结束运行。
/**
* 线程状态--线程等待唤醒状态的相互转换
*
* @author zhuhuix
* @date 2020-05-10
*/
public class ThreadStatus {
//创建静态锁对象
static final Object objectLock = new Object();
//线程结束标志
static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
//调用等待线程执行
new Thread(new Waiting()).start();
TimeUnit.SECONDS.sleep(1);
//调用唤醒线程执行
new Thread(new Notify()).start();
}
/**
* 等待线程
*/
static class Waiting implements Runnable {
@Override
public void run() {
synchronized (objectLock) {
while (flag) {
try {
System.out.println(Thread.currentThread().getName() + "线程正在等待:"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "线程完成运行:"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
}
}
}
/**
* 唤醒线程
*/
static class Notify implements Runnable {
@Override
public void run() {
synchronized (objectLock) {
System.out.println(Thread.currentThread().getName() + "线程唤醒:"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
objectLock.notify();
flag = false;
}
}
}
}
输出结果:
线程的计时等待
- 线程在休眠时加上计时,无需再被被其他唤醒,计时时间到了,自行醒来。
/**
* 线程状态--线程的计时等待
*
* @author zhuhuix
* @date 2020-05-10
*/
public class ThreadWaitAndNotify {
//创建静态锁对象
static final Object objectLock = new Object();
public static void main(String[] args) {
//调用等待线程执行
new Thread(new ThreadWaitAndNotify.Waiting()).start();
}
/**
* 等待线程
*/
static class Waiting implements Runnable {
@Override
public void run() {
synchronized (objectLock) {
try {
System.out.println(Thread.currentThread().getName() + "线程正在等待:"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
objectLock.wait(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "线程完成运行:"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
}
}
}
线程等待/唤醒机制的简单总结
等待方:
synchronized (对象) {
while (条件不满足) {
等待
}
运行业务逻辑
}
通知方:
synchronized (对象) {
改变条件
唤醒
}
等待/唤醒机制的应用–生产者与消费者
- 生产者生产披萨,每生产满10个,通知消费者购买。
- 消费者购买披萨,若无披萨则等待生产者通知。
/**
* 线程等待唤醒状态的应用--生产者与消费者问题
*
* @author zhuhuix
* @date 2020-05-10
*/
public class ProduceConsumeThread {
//创建静态锁对象
static final Object objectLock = new Object();
//披萨数量
static int pizza = 0;
public static void main(String[] args) {
//生产者生产披萨
new Thread(new ProduceConsumeThread.Produce()).start();
//消费者购买披萨
new Thread(new ProduceConsumeThread.Consume()).start();
}
/**
* 生产者
*/
static class Produce implements Runnable {
@Override
public void run() {
while (true) {
synchronized (objectLock) {
if (pizza >= 10) {
//通知消费者购买
objectLock.notifyAll();
} else {
pizza++;
System.out.println(Thread.currentThread().getName() + "生产出披萨:" + pizza + "个,"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* 消费者
*/
static class Consume implements Runnable {
@Override
public void run() {
while (true) {
synchronized (objectLock) {
if (pizza == 0) {
try {
System.out.println(Thread.currentThread().getName() + "消费者正在等待购买:"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
pizza--;
System.out.println(Thread.currentThread().getName() + "消费者购买披萨," + "披萨还剩" + pizza + "个,"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
}
}
}
}
}
}
运行结果如下:
上一篇: Java并发编程之CAS应用
下一篇: 使用指定jdk运行jar包