java线程分类

在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)

用户线程和守护线程的区别

二者其实基本上是一样的。唯一的区别在于JVM何时离开。

  • 用户线程:当存在任何一个用户线程未离开,JVM是不会离开的。

  • 守护线程:如果只剩下守护线程未离开,JVM是可以离开的。

在Java中,制作守护线程非常简单,直接利用setDaemon(true)

用户线程

平时用到的普通线程均是用户线程,当在Java程序中创建一个线程,它就被称为用户线程

标准定义

不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。

优点

1.线程位于用户空间(即不需要模式切换)。
2.完全控制线程调度器(例如:网站服务器)。
3.独立于操作系统(线程可以在不支持它们的操作系统上运行)。
4.运行时系统(run time system)可以切换用户空间中的本地阻塞线程(例如:等待另一个线程完成).

缺点

1.系统调度中,对一个线程的阻塞将会导致整个进程阻塞;(例如:当一个线程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会)
2.网站服务器中,一个页面的错误将导致整个进程阻塞。
3.非真正意义的线程并行(一个进程安排在单个CPU上)。
4.不存在时钟中断(例如,如果用户线程是非抢占式(non-preemptive)的,将无法被“进程调度”(schedulers)以round-robin的调度算法调用,因为round-robin调度算法中限制了cpu时间片)。
用户线程不需要额外的内核开支,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是;而内核线程则没有这个限制,有利于发挥多处理器的并发优势,但却占用了更多的系统开支。

抢占式和非抢占式

非抢占式是一种进程调度的方式,让原来正在运行的进程继续运行,直至该进程完成或发生某种事件(如I/O请求),才主动放弃处理机,让进程运行直到结束或阻塞的调度方式,容易实现,抢占式(Preemptive)
允许将逻辑上可继续运行的在运行过程暂停的调度方式
可防止单一进程长时间独占CPU
系统开销大(降低途径:硬件实现进程切换,或扩充主存以贮存大部分程序)

抢占式与非抢占式的区分

一个新创建的进程首先被放置在Ready队列,它一直等待执行的机会。一旦内核调度器将CPU分配给它开始执行时,有四种可能:
(1)进程主动发起I/O请求,但I/O设备还没有准备好,所以会发生I/O阻塞,进程进入Wait状态。
(2)内核分配给进程的时间片已经耗尽了,进程进入Ready状态,等待内核重新分配时间片后的执行机会。
(3)进程创建了子进程,并调用wait()等待子进程执行完毕,进程就重新进入Ready状态等待阻塞结束。
(4)I/O设备可以在任意时刻发生中断,CPU会停下当前正在执行的进程去处理中断,因此进程进入Ready状态。
区分一个多任务分时系统是抢占式的还是非抢占式的,则要看进程调度能否在(4)发生中断,CPU停止当前手头的工作(正在执行的进程),保存下当前工作的现场后,转入中断处理程序。如果在中断处理程序的执行中能否发生调度,即中断处理程序还没有执行完,又切换到其他进程。这里要说明的是,系统调用也是通过中断机制来实现的。所以,也就是说要看系统调用的执行过程中,或者中断处理程序的执行过程中能否发生调度(抢占)。

守护线程

定义:是个服务线程,准确地来说就是服务其他的线程,这是它的作用——而其他的线程只有一种,那就是用户线程。所以java里线程分2种

  1. 守护线程,比如垃圾回收线程,就是最典型的守护线程。
  2. 用户线程,就是应用程序里的自定义线程

代码举例

用户线程的创建

1
2
3
4
5
6
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("3.我是通过匿名内部类方式启动的线程");
}
}).start();

守护线程的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DamomThread {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread( ()-> {
while (true){
try {
Thread.sleep(1000);
System.out.println("内部线程:正在执行。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//开启守护进程
//thread.setDaemon(true);
thread.start();
Thread.sleep(2_000L );
System.out.println("Main thread finished lifecycle !!!!!!!!!!");
}
}

该代码一共开启了两个线程,一个是main线程,另一个是里面执行的线程thread

未开始守护进程: thread.setDaemon(false);

外面的线程main线程,结束退出,里面的线程,依旧继续执行,如截图

开始守护进程: thread.setDaemon(true);

main线程退出,内部线程一起退出