状态
在不同的语言中,线程状态定义有细微的不同,但是都是大同小异.在java中,在Thread
类中有一个枚举,记载了线程的6个状态.
-
NEW(初始)
:新创建一个线程,但是还没有调用线程的start()方法. -
RUNNABLE(可运行)
:在java中,把运行running和就绪ready状态都称为RUNABLE状态.在创建完线程后,调用线程的start()方法,线程将进入该状态.该线程在线程池中等待系统调度获取cpu使用权时,就是ready.获取到cpu使用权时,就是running. -
BLOCKED(阻塞)
:线程在等待一个锁而无法进入同步块. -
WAITING(等待)
:一个正在无限等待另一个线程执行一个特别的动的线程就处于该状态. -
TIMED_WAITING(超时等待)
:一个正在限时等待另一个线程执行一个动作的线程处于这一状态.基本上与WAITING一样,只不过多了时限而已. -
TERMINATED(终止)
:已退出的线程处于这种状态.
状态变化
线程状态转换图.jpgBLOCKED(阻塞)
线程何时会进入BLOCKED
状态呢?总结下来就是线程在等待一个监视锁,这个时候就会进入该状态.例如下面代码:
public class BlockDemo1 {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> counter.increase());
t1.setName("线程1");
t1.start();
//确保线程1先获取到锁
Thread.sleep(1000);
Thread t2 = new Thread(() -> counter.increase());
t2.setName("线程2");
t2.start();
//确保线程2已经开始运行
Thread.sleep(1000);
//打印线程2状态
System.out.println("t2.getState() = " + t2.getState());
}
}
class Counter {
int number = 0;
public synchronized void increase(){
number++;
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上面的示例代码中,线程1先获取到counter实例锁
,而线程2没有获取到锁,这将引发线程2进入BLOCKED状态.这是一种比较常见的情况.另外还有一种情况,如下代码所示:
public class BlockDemo2 {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
Thread t1 = new Thread(() -> account.get(100));
t1.setName("取钱线程");
t1.start();
Thread.sleep(500L);
Thread t2 = new Thread(() -> account.add(100));
t2.setName("存钱线程");
t2.start();
Thread.sleep(500L);
//
System.out.println(t1.getName()+":"+t1.getState());
}
}
class Account{
private int amount;
/**
* 存入金额
* @param cash
*/
public synchronized void add(int cash){
System.out.println(Thread.currentThread().getName()+":准备存钱");
amount = amount + cash;
notify();
try {
//模拟化时间再做一些事
Thread.sleep(10000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 取钱
* @param cash
*/
public synchronized void get(int cash){
System.out.println(Thread.currentThread().getName()+":准备取钱");
while (amount <= cash){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
amount = amount - cash;
}
}
这是第二种导致进入BLOCKED状态的情况.取钱线程先获取到对象锁进入取钱代码块中,由于金额不够,调用wait()等待金额够了再取钱.因为调用wait()失去了对象锁,存钱线程执行.存钱线程存入金额,然后调用了notify(),这个时候取钱线程已唤醒,但是因为存钱线程还未执行完,取钱线程等待锁,所以进入BLOCKED状态.这个就是后面说的一个处于 blocked 状态的线程正在等待一个监视器锁,在其调用 Object.wait 方法之后,以再次进入一个同步的块或方法
的解释.
WAITING(等待)
线程进入WAITING状态的情况有很多.常见的有以下几种:
- 当前线程调用对象的wait()方法,在等待另外一个线程调用notify()或者notifyAll()
- 在一个线程中,另一个线程调用了join()方法,该线程会一直等待另外一个线程执行完.
- LockSupport.park
代码示例1:当线程1调用wait(),线程1进入WAITING状态.线程2调用notify()唤醒线程1.线程1继续执行.
public class WaitingDemo1 {
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
Thread t1 = new Thread(() -> {
synchronized (o){
System.out.println("线程1开始执行wait()");
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1执行完成");
}
});
t1.start();
//确保线程1先执行
Thread.sleep(100);
Thread t2 = new Thread(() -> {
synchronized (o){
System.out.println("线程2开始执行");
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2调用notify()");
o.notify();
}
});
t2.start();
System.out.println("线程1状态:"+t1.getState());
}
}
示例2:在线程1中,另起一个线程线程2.当线程2未执行完时,线程1的状态始终都是WAITING.
public class WaitingDemo2 {
public static void main(String[] args) throws InterruptedException {
// 线程1
Thread t1 = new Thread(() -> {
//线程2
Thread t2 = new Thread(() -> {
while (true){
}
});
t2.setName("线程2");
t2.start();
try {
//线程2调用join
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.setName("线程1");
t1.start();
//确保线程1和线程2已开始运行
Thread.sleep(500L);
System.out.println(t1.getState());
}
}
blocked往往是因为监视锁无法获取,导致无法进入代码块.而waiting主要用来线程之间的协调,进入waiting一般需要另外一个线程执行notify()或者notifyAll()才能被唤醒.blocked更像是被动阻塞的,而waiting更像是主动阻塞的.
TIMED_WAITING(超时等待)
大体上与WAITING相似,不同的主要在该状态有超时,如果时间过后,将自动解除该状态.
- Thread.sleep
- 带时限(timeout)的 Object.wait
- 带时限(timeout)的 Thread.join
- LockSupport.parkNanos
- LockSupport.parkUnti
上述方式都会让线程进入该状态.
进入TIMED_WAITING其中的sleep()方法,单独的线程调用也可,不一定非要其他线程协同.与wait()不同的是,它不需要同步语义.例如一般调用wait()都是在同步代码块中,如果不在会报异常.但是sleep()是不需要的.同时,在调用sleep时,是不会让出锁的,而wait()是会让出锁的.
网友评论