线程状态
概述
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中, 有几种状态呢?在
java.lang.Thread.State 这个枚举中给出了六种线程状态:
public class Thread{
...
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
...
}
线程状态
|
导致状态发生条件
|
NEW(
新建
)
|
线程刚被创建,但是还没调用start方法,并未启动。
|
Runnable(可运行
)
|
线程可以在
java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
|
Blocked(锁阻塞
)
|
当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入
Blocked
状
态;当该线程持有锁时,该线程将变成
Runnable
状态。
|
Waiting(无限等待
)
|
一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入
Waiting
状态。进入这个
状态后是不能自动唤醒的,必须等待另一个线程调用
notify
或者
notifyAll
方法才能够唤醒。
|
Timed Waiting(计时等待
)
|
同
waiting
状态,有几个方法有超时参数,调用他们将进入
Timed Waiting状态。这一状态 将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有
Thread.sleep
、
Object.wait
。
|
Teminated(被终止
)
|
因为
run
方法正常退出而死亡,或者因为没有捕获的异常终止了
run
方法而死亡。
|
Timed Waiting(计时等待)
Timed Waiting
在
API
中的描述为:一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。
为了让其他线程有机会执行,可以将
Thread.sleep()
的调用
放线程
run()
之内。这样才能保证该线程执行过程中会睡眠。
sleep
与锁无关,线程睡眠到期自动苏醒。
sleep()
中指定的时间是线程不会运行的最短时间。因此,
sleep()
方法不能保证该线程睡眠到期后就
开始立刻执行。
BLOCKED(锁阻塞)
Blocked
状态在
API
中的介绍为:一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态。
比如,线程
A
与线程
B
代码中使用同一锁,如果线程
A获取到锁,线程
A
进入到
Runnable
状态,那么线程
B
就进入到
Blocked
锁阻塞状态。
Waiting(无限等待)
Wating
状态在
API
中介绍为:一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。
当多个线程协作时,比如
A
,
B
线程,如果
A
线程在
Runnable
(可运行)状态中调用了
wait()
方法那么
A线程就进入 了
Waiting
(无限等待)状态,同时失去了同步锁。假如这个时候
B线程获取到了同步锁,在运行状态中调用了notify()
方法,那么就会将无限等待的
A
线程唤醒。
注意是唤醒,如果获取到锁对象,那么A线程唤醒后就进入 Runnable(可运行)状态;如果没有获取锁对象,那么就进入到Blocked(锁阻塞状态)。
什么是线程通信、如何实现?
所谓线程通信就是线程间相互发送数据,线程间共享一个资源即可实现线程通信。
线程通信常见形式
通过共享一个数据的方式实现。
根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做。
线程通信实际应用场景
生产者与消费者模型:生产者线程负责生产数据,消费者线程负责消费生产者产生的数据。
要求:生产者线程生产完数据后唤醒消费者,然后进入等待状态,消费者消费完该数据后唤醒生产者,然后进入等待状态。
Object类的等待和唤醒方法
注意:上述方法应该使用当前同步锁对象进行调用。
线程通信案例一(模拟客服系统)
模拟客服系统,系统可以不断的接入电话 和 分发给客服。
线程通信的前提:线程通信通常是在多个线程操作同一个共享资源的时候需要进行通信,且要保证线程安全。
/**
呼叫系统。
*/
public class CallSystem {
// 定义一个变量记录当前呼入进来的电话。
public static int number = 0; // 最多只接听一个。
/* 接入电话
*/
public synchronized static void call() {
try {
number++;
System.out.println("成功接入一个用户,等待分发~~~~");
// 唤醒别人 : 1个
CallSystem.class.notify();
// 让当前线程对象进入等待状态。
CallSystem.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
分发电话
*/
public synchronized static void receive() {
try {
String name = Thread.currentThread().getName();
if(number == 1){
System.out.println(name + "此电话已经分发给客服并接听完毕了~~~~~");
number--;
// 唤醒别人 : 1个
CallSystem.class.notify();
CallSystem.class.wait(); // 让当前线程等待
}else {
// 唤醒别人 : 1个
CallSystem.class.notify();
CallSystem.class.wait(); // 让当前线程等待
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class CallThread extends Thread{
@Override
public void run() {
// 不断的打入电话
while (true){
CallSystem.call();
}
}
}
/**
接电话线程类
*/
public class ReceiveThread extends Thread{
@Override
public void run() {
// 1号 2号
while (true){
CallSystem.receive();
}
}
}
public class TestDemo {
public static void main(String[] args) {
// 1、生产者线程:负责不断接收打进来的电话
CallThread call = new CallThread();
call.start();
// 2、消费者线程:客服,每个客服每次接听一个电话
ReceiveThread r1 = new ReceiveThread();
r1.start();
}
}
线程通信案例二(吃包子)
/*
进入到TimeWaiting(计时等待)有两种方式
1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态
唤醒的方法:
void notify() 唤醒在此对象监视器上等待的单个线程。
void notifyAll() 唤醒在此对象监视器上等待的所有线程。
*/
public class Demo02WaitAndNotify {
public static void main(String[] args) {
//创建锁对象,保证唯一
Object obj = new Object();
// 创建一个顾客线程(消费者)
new Thread(){
@Override
public void run() {
//一直等着买包子
while(true){
//保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("顾客1告知老板要的包子的种类和数量");
//调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行的代码
System.out.println("包子已经做好了,顾客1开吃!");
System.out.println("---------------------------------------");
}
}
}
}.start();
// 创建一个顾客线程(消费者)
new Thread(){
@Override
public void run() {
//一直等着买包子
while(true){
//保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("顾客2告知老板要的包子的种类和数量");
//调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行的代码
System.out.println("包子已经做好了,顾客2开吃!");
System.out.println("---------------------------------------");
}
}
}
}.start();
//创建一个老板线程(生产者)
new Thread(){
@Override
public void run() {
//一直做包子
while (true){
//花了5秒做包子
try {
Thread.sleep(5000);//花5秒钟做包子
} catch (InterruptedException e) {
e.printStackTrace();
}
//保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("老板5秒钟之后做好包子,告知顾客,可以吃包子了");
//做好包子之后,调用notify方法,唤醒顾客吃包子
//obj.notify();//如果有多个等待线程,随机唤醒一个
obj.notifyAll();//唤醒所有等待的线程
}
}
}
}.start();
}
}