Java中的线程
一个Java程序从main()方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java程序天生就是多线程程序,因为执行main()方法的是一个名称为main的线程。
下面使用JMX来查看一个普通的Java程序包含哪些线程:
public class MultiThread {
public static void main(String[] args) {
//获取Jva线程管理MXBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
//不需要获取同步的monitor和synchronized信息,仅获取线程和线程堆栈信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
//遍历进程信息,仅打印线程ID和线程名称信息
for(ThreadInfo threadInfo : threadInfos ){
System.out.println("[" + threadInfo.getThreadId() + "]" + threadInfo.getThreadName());
}
}
}
输出如下:
[6]Monitor Ctrl-Break //Idea独有,因为Idea使用反射机制执行代码,与此同时会创建一个Monitor Ctrl-Break的线程用于监控的目的。
[5]Attach Listener //该线程是负责接收到外部的命令,执行该命令,并且把结果返回给发送者。通常我们会用一些命令去要求jvm给我们一些反馈信息,如:java -version、jmap、jstack等等。如果该线程在jvm启动的时候没有初始化,那么,则会在用户第一次执行jvm命令时,得到启动。
[4]Signal Dispatcher //第一个Attach Listener线程的职责是接收外部jvm命令,当命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather线程也是在第一次接收外部jvm命令时,进行初始化工作。
[3]Finalizer // JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理;最后将该Finalizer对象的引用置为null,由垃圾收集器来回收。
[2]Reference Handler //清除Reference的线程,它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。
[1]main //主线程
为什么使用多线程
- 更多的处理核心
- 更快的响应时间
- 更好的编程模型
线程的优先级
在Java中,通过一个整型成员变量priority来控制优先级,优先级的范围从1 ~ 10,在线程构建的时候可以通过setPriority(int) 方法来修改优先级,默认优先级为5,优先级高的线程分配时间片的数量多于优先级低的线程。
- 针对频繁阻塞(休眠或高I/O)的线程设置较高优先级
- 偏重计算(需要较多CPU时间或者偏运算)的线程设置较低优先级
注意:不同JVM以及操作系统上,线程规划存在差异,有些系统甚至会忽略对线程优先级的设定。
线程的状态
Java线程在运行的生命周期中可能处于下表所示的6种不同状态,在给定的一个时刻,线程只能处于其中的一个状态。
状态名称 | 说明 |
---|---|
NEW | 初始状态,线程被构建,但是还没有调用start()方法 |
RUNNABLE | 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统的称为“运行中” |
BLOCKED | 阻塞状态,表示线程被阻塞 |
WAITING | 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) |
TIME_WAITING | 超时等待状态,该状态不同于WAITING,它是可以在指定的时间自行返回的 |
TERMINATED | 终止状态,表示当前线程已经执行完毕 |
示例:
package pers.zhang.part4;
import jdk.nashorn.internal.ir.Block;
/**
* @author zhang
* @date 2020/1/15 - 22:15
* 线程状态
*/
public class ThreadState {
public static void main(String[] args) {
new Thread(new TimeWaiting(), "TimeWaitingThread").start();
new Thread(new Waiting(), "WaitingThread").start();
//使用两个Blocked线程,一个获取锁成功,另一个被阻塞
new Thread(new Blocked(), "BlockedThread-1").start();
new Thread(new Blocked(), "BlockedThread-2").start();
}
//该线程不断的睡眠
static class TimeWaiting implements Runnable{
@Override
public void run() {
while(true){
SleepUtils.second(100);
}
}
}
//该线程在Waiting.class实例上等待
static class Waiting implements Runnable{
@Override
public void run() {
while(true){
synchronized(Waiting.class){
try{
Waiting.class.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
//该线程在Blocked.class实例上加锁后,不会释放锁
static class Blocked implements Runnable{
@Override
public void run() {
synchronized (Blocked.class){
while(true){
SleepUtils.second(100);
}
}
}
}
}
package pers.zhang.part4;
import java.util.concurrent.TimeUnit;
/**
* @author zhang
* @date 2020/1/15 - 22:20
*/
public class SleepUtils {
public static final void second(long seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行程序,然后再终端或者命令提示符中:键入“jps”,输出如下:
//进程Id每次都不同
12424 Launcher
19148
2604 Jps
28476 ThreadState
再次键入"jstack 28476(对应自己的)",部分输出如下:
//用于监控非daemon线程,只有daemon线程时,退出JVM。正常运行中!
"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000002f42800 nid=0x6610 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
//BlockedThread-1获取到了Blocked.class的锁
"BlockedThread-2" #14 prio=5 os_prio=0 tid=0x0000000019026800 nid=0x30bc waiting on condition [0x0000000019e6e000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
//BlockedThread-1线程阻塞在获取Blocked.class示例的锁上
"BlockedThread-1" #13 prio=5 os_prio=0 tid=0x0000000019026000 nid=0x72e8 waiting for monitor entry [0x0000000019d6f000]
java.lang.Thread.State: BLOCKED (on object monitor)
//WaitingThread线程在Waiting实例上等待
"WaitingThread" #12 prio=5 os_prio=0 tid=0x0000000019022000 nid=0x67d4 in Object.wait() [0x0000000019c6f000]
java.lang.Thread.State: WAITING (on object monitor)
//TimeWaitingThread线程处于超时等待状态
"TimeWaitingThread" #11 prio=5 os_prio=0 tid=0x000000001901f800 nid=0x36d0 waiting on condition [0x0000000019b6e000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
Java线程状态变迁
- 线程创建之后,调用start()方法开始运行。当线程执行wait()方法之后,线程进入等待状态。
- 进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态。
- 超时等待相当于在等待状态的基础上加了超时显示,也就是超时时间到达时将返回到运行状态。
- 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到阻塞状态。
- 线程在执行Runnable的run()方法之后将会进入到终止状态。