首先,我们知道 java中的线程分为两类: 守护线程和用户线程(非守护线程)。
用户线程即是运行在前台的线程,而守护线程顾名思义就是监控守护用户线程的线程,是为运行在前台的用户线程提供运行便利服务的,且只有在用户线程运行时才需要。比如垃圾回收线程。当VM检测到用户线程已经退出运行,仅剩守护线程时,VM会退出,因为没有被守护者,程序运行也就没有必要了。
守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。
一般我们创建的都是用户线程。但也有例外,比如在Junit环境中创建的多线程都会编程守护线程模式。
例子:
@Test
public void threadTest(){
final char[] chars1 = "123".toCharArray();
final char[] chars2 = "456".toCharArray();
new Thread(()->{
synchronized (chars1){
for (int i = 0; i < chars1.length; i++) {
System.out.println(chars1[i]);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (chars2){
for (int i = 0; i < chars2.length; i++) {
System.out.println(chars2[i]);
}
}
}
}).start();
new Thread(()->{
synchronized (chars2){
for (int i = 0; i < chars2.length; i++) {
System.out.println(chars2[i]);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (chars1){
for (int i = 0; i < chars1.length; i++) {
System.out.println(chars1[i]);
}
}
}
}).start();
}
上面代码写了一个死锁的例子,运行后发现程序并没有循环等待状态,而是直接运行结束。是因为@test函数是不会等待子守护线程运行结束,只要@test函数执行结束,程序就立即结束。
如果换成在main函数中创建,则创建的都是用户线程,子用户线程未执行结束,则main函数线程会一直处于等待状态,这个进程也就不会结束。
public static void main(String[] args) {
final char[] chars1 = "123".toCharArray();
final char[] chars2 = "456".toCharArray();
new Thread(()->{
synchronized (chars1){
for (char c : chars1) {
System.out.println(c);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (chars2){
for (char c : chars2) {
System.out.println(c);
}
}
}
}).start();
new Thread(()->{
synchronized (chars2){
for (char c : chars2) {
System.out.println(c);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (chars1){
for (char c : chars1) {
System.out.println(c);
}
}
}
}).start();
}
执行代码,会出现死锁等待现象。
注:在@test函数下,可以在test方法结尾手动添加awaitTermination(),让主线程等待子用户线程。