Java 线程状态

问题

1
2
同一个线程能不能start执行2次?
---不能

Java虚拟机所暴露的线程状态,与操作系统底层的线程状态是两个不同层面的事

JVM线程状态

JVM中定义的线程状态有以下几种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public enum State {
/**
* 线程刚创建,还未start
*/
NEW,
/**
* 线程正在jvm中执行,对操作系统来说可能正在等待时间片执行(就绪态)
* I/O阻塞或网络等待,对应的JVM线程状态也是RUNNABLE
*/
RUNNABLE,
/**
* 程正在等待获取锁,阻塞状态
*/
BLOCKED,
/**
* 一般通过Object.wait()或者Thread.join(),进入waiting状态;前提是这个线程已经拥有锁了
* 如果一个线程调用了一个对象的wait方法,那么这个线程就会处于waiting状态直到另外一个线程调用这个对
* 象的notify或者notifyAll方法后才会解除这个状态
*/
WAITING,
/**
* 通过sleep(timeout)或wait(timeout)方法进入的限期等待的状态
* 需要注意2者区别
*/
TIMED_WAITING,
/**
* 线程执行结束
*/
TERMINATED;
}

blockedwaiting状态的区别

  • blocked是虚拟机认为程序还不能进入某个区域,因为同时进去就会有问题,这是一块临界区
  • wait操作的先决条件是要进入临界区,也就是线程已经拿到锁了,自己可能进去做了一些事情,但此时通过判定业务上的参数,发现还有一些其他配合的资源没有准备充分,那么自己就等等再做其他事情。

sleep()wait()操作的区别

  • sleepThread类的静态方法,waitObject类中定义的方法
  • Thread.sleep不会导致锁行为的改变,如果当前线程是拥有锁的,那么Thread.sleep不会让线程释放锁;Object.wait会释放锁,进入等待队列中
  • wait不带计时参数是WAITING状态,带计时参数是TIMED_WAITING状态;不管是通过notify()唤醒还是超时时间已到,都需要重新获取锁才能继续执行

线程start方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public synchronized void start() {
// threadStatus=0,表示当前为NEW状态
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
// 本地方法执行线程
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}

start()方法是同步的,而且执行前会判断当前线程状态是否为NEW;也就是说,同一个线程只能执行一次

网络阻塞线程状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class testBlockedSocketState {
public static void main(String[] args) throws InterruptedException {
Thread serverThread = new Thread(new Runnable() {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(10086);
while (true) {
// 阻塞的accept方法
Socket socket = serverSocket.accept();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}, "socket线程"); // 线程的名字
serverThread.start();
// 确保run已经得到执行
Thread.sleep(500);
// 状态为RUNNABLE
System.out.println(serverThread.getState());
}

使用jstack查看线程状态:

1
2
3
4
5
6
7
8
"socket线程" #9 prio=5 os_prio=31 tid=0x00007ff0bc026800 nid=0x4c03 runnable [0x00007000010c6000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at com.zsr.test.thread.ThreadBlockedIOState$1.run(ThreadBlockedIOState.java:19)
at java.lang.Thread.run(Thread.java:745)

可见JVM线程状态为RUNNABLE;I/O 阻塞也是如此

注意:进行传统上的 IO 操作时,我们也会说“阻塞”,但这个“阻塞”与线程的BLOCKED 状态是两码事

1
当进行阻塞式的 IO 操作时,底层的操作系统线程确实处在阻塞状态

参考

Java线程状态分析

Java 线程状态之 RUNNABLE

What is difference between wait and sleep in Java?

热评文章