线程的概念:
程序、进程、线程、多 线程
程序(Program)是为完成特定任务、用某种语言编写的一组指令的集合,指一段静态的代码。作为一个静态文件存储在计算机系统的硬盘等存储空间中
进程(Process)是正在被操作系统运行的应用程序
线程(Thread)是指进程中的一个执行流程。一个进程可以由多 个线程组成,即在一个进程中可以同时运行多个不同的线程,这些线程共享进程的内存空间,分别执行不同的任务。
多线程的概念:
多 线程指在单个程序中可以同时运行多 个不同的线程,执行不同的任务,这是实现并发机制的一种有效手段
操作系统使用分时管理各个进程,按CPU时间片轮流执行每个进程;Java的多
public class My1stThreadExtendsThread extends Thread {
public static void main(String[] args) {
// 获取当前线程对象,这里是主线程对象
Thread t = Thread.currentThread();
out.println("主线程是:" + t);
// 创建子线程并启动
My1stThreadExtendsThread mt = new My1stThreadExtendsThread();
mt.start();
}
// 重写父类的run()方法
public void run() {
// 获取当前线程对象,这里是子线程对象
System.out.println("子线程是:" + Thread.currentThread());
// 完成游戏的倒计时功能
…………
}
}
线程就是在操作系统每次分时给Java程序一个时间片的CPU时间内,在若干独立的可控制的线程之间切换
多 线程的目的是为了最大限度的利用CPU资源
主线程:
当Java程序启动时,一个线程立刻运行,该线程通常叫做程序的主线程(main thread)。每个Java程序都有一个隐含的主线程。
主线程的重要性
A、它是产生其他子线程的线程。
B、一般来讲,主线程最先启动且最后完成执行(因为很多时候主线程需要完成各种关闭动作),但并不总是这样的
尽管主线程在程序启动时自动创建,但它可以由一个Thread对象控制
import static java.lang.System.out;//静态导入
public class My1stMainThread extends Thread {
public static void main(String[] args) {
Thread t = Thread.currentThread();//获取当前线程对象,这里是主线程
out.println("当前线程是:" + t);
t.setName("主线程");// 改变主线程内部名称
out.println("当前线程是:" + t);
try {
for (int i = 0; i < 5; i++) {
out.println(i);
Thread.sleep(1000);// 线程睡眠一秒钟
}
} catch (InterruptedException ex) {
out.println("主线程中断!");
ex.printStackTrace();
}
}
}
线程的创建和启动:
创建线程有两种方式:
A、定义一个Thread类的子类,覆盖Thread类的run()方法,然后创建该子类的实例。
B、定义一个实现Runnable接口的类,实现它的run()方法,然后将这个类的实例作为Thread类构造方法的参数,创建Thread类的实例
调用start()方法启动线程,不要直接调用run方法,直接调用run方法仅仅是将run作为一个普通的实例方法来调用而不会启动一个线程
{通过扩展Thread类来创建线程}
public class My1stThreadExtendsThread extends Thread {
public static void main(String[] args) {
// 获取当前线程对象,这里是主线程对象
Thread t = Thread.currentThread();
out.println("主线程是:" + t);
// 创建子线程并启动
My1stThreadExtendsThread mt = new My1stThreadExtendsThread();
mt.start();
}
// 重写父类的run()方法
public void run() {
// 获取当前线程对象,这里是子线程对象
System.out.println("子线程是:" + Thread.currentThread());
// 完成游戏的倒计时功能
…………
}
}
通过实现Runnable接口来创建线程
public class My1stThreadImplRunnable implements Runnable {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();// 获取主线程对象
out.println("主线程是:" + mainThread);
My1stThreadImplRunnable mt = new My1stThreadImplRunnable();
Thread t1 = new Thread(mt);// 创建一个子线程对象
t1.start();// 启动子线程
}
//重写父类的run()方法
public void run() {
out.println("子线程是:" + Thread.currentThread());
// 完成游戏的倒计时功能
…………
}
}
创
建线程两种方法的区别:
区别:Java中通过实现Runnable接口创建线程的方法更常用,因为Java不支持多继承,有些类需要继承其他类来完成功能,无法再继承Thread类完成多线程功能,此时必须通过实现一个接口来增加多线程功能。
相同点:都要通过Thread类才能完成线程的创建
线程启动:
尽管线程执行的是 run()方法中的代码,但实际上是通过 start()方法触发的,而不是直接调用run方法
用start方法来启动线程,真正实现了多线程运行
run()方法只是类的一个普通方法而已,如果直接调用run()方法,程序中依然只有主线程这一个线程
总结:调用start()方法方可启动线程,而run()方法只是thread的一个普通方法调用
线程的生命周期:
同进程一样,线程也有从创建、运行到消亡的过程,这称为线程的生命周期。
创建状态
用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于创建状态,这是线程已被创建但未开始执行的特殊状态。处于创建状态的线程是一个空的线程对象,系统不为它分配资源,但有自己的内存空间。创建状态的线程通过调用start()方法进入就绪状态。
就绪状态
处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,因而将进入线程队列,等待系统为其分配CPU。
线程根据自身优先级进入等待队列的相应位置。
一旦获得CPU,线程就进入运行状态,并自动调用自己的run()方法。
运行状态
进入运行状态的线程顺序执行自己run()方法中的代码,直到调用其他方法而终止,或等待某资源而阻塞,或完成任务而死亡。
运行状态表示线程拥有了对处理器的控制权,其代码正在运行,除非运行过程的控制权被另一优先级更高的线程抢占,否则这个线程将一直持续到运行完毕
阻塞状态
处于运行状态的线程在某些情况下让出CPU并暂时终止自己的运行,进入阻塞状态。
A、执行了sleep()方法
B、执行了wait()方法
C、等待I/O设备等资源
在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时才能转入就绪状态。
A、睡眠时间已到
B、其它线程通知阻塞线程
C、等待的I/O设备空闲
当再次获得CPU时,从原来终止的位置开始继续运行。
死亡状态
这是线程生命周期中的最后一个阶段,表示线程已退出运行状态,并且不再进入就绪队伍。当线程的run()方法结束或由于其他原因被终止后,线程就进入消亡状态。线程的终止分两种情况:
A、自然死亡,即从线程的run()方法正常退出;
B、使用Thread类中的destroy()方法或stop()方法强行终止
注意:Java系统不赞成使用destroy()方法或stop()方法终止线程,因为若使用不当,可能会导致程序的死锁。
线程优先级:
Java中的线程优先级是在Thread类中定义的常量
NORM_PRIORITY:值为5
MAX_PRIORITY:值为10
MIN_PRIORITY:值为1
缺省优先级为:NORM_PRIORITY
有关优先级的方法有两个:
final void setPriority(int newp):修改线程的当前优先级
final int getPriority():返回线程的优先级
class NewThread extends Thread {
private long count = 0; //计数器,统计线程执行的次数
private boolean isPass = true; //定义一个标志,用来终止循环
NewThread(String name) {
super(name);
}
public void run() {
while (isPass) { //isPass为假时将中止循环,否则count不断的加1
count++;
}
}
public long result() { //返回count的值
return count;
}
public void stopThread() { //中止线程
isPass = false;
}
}
public class MultiThreadDemo4 {
public static void main(String[] args) {
NewThread t1 = new NewThread("Thread 1");
NewThread t2 = new NewThread("Thread 2");
t1.setPriority(Thread.NORM_PRIORITY + 1); // 设置优先级为6
t2.setPriority(Thread.NORM_PRIORITY + 3); // 设置优先级为8
t1.start(); // 启动线程t1
t2.start(); // 启动线程t2
try {
Thread.sleep(500); // 主线程睡眠500毫秒
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
System.out.println("Thread 1:Priority is " + t1.getPriority()
+ " Result of Count is: " + t1.result());
System.out.println("Thread 2:Priority is " + t2.getPriority()
+ " Result of Count is: " + t2.result());
t1.setPriority(Thread.MAX_PRIORITY); // 重新设置t1的优先级为最大
try {
Thread.sleep(500); // 主线程睡眠500毫秒
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
t1.stopThread();//中止线程t1
t2.stopThread();//中止线程t2
System.out.println("After the priority of Thread 1 is changed: ");
System.out.println("Thread 1:Priority is " + t1.getPriority()
+ " Result of Count is: " + t1.result());
System.out.println("Thread 2:Priority is " + t2.getPriority()
+ " Result of Count is: " + t2.result());
}
}
从上面的测试可以看出优先级并不能决定线程绝对的占用CPU,即高优先级的线程不可能一直占用CPU的运行,低优先级的线程也不会一直闲着,实际上,线程之间的优先级并不能决定线程间本身的执行顺序
Java中的线程调度采用的是抢占式调度,即处在就绪队列中的线程(不论优先级高还是低)一律遵循抢占模式,当我们将一个线程的优先级设置的较高时仅仅是”建议”操作系统能够为它分配更多的处理时间,但这并不代表它就一定会马上占用CPU,因为它仍然需要与其它线程以公平的方式进行抢占式竞争
JVM之所以不能通过优先级来确定线程的执行顺序是因为Java的线程最终都是映射成操作系统的本地化内核线程来完成的,线程的调度由操作系统说了算,而不是JVM
我们不能编写依赖于线程优先级的操作代码,这样做是危险的,针对这个问题,Java提供了更高层一级的同步化控制,这个将在后面讲述到
线程的休眠
在现实问题域中,有可能需要等待一个线程执行结束后再运行另一个线程,这时可以利用Java提供的两种方法来实现这个功能。调用线程类的isAlive() 方法和sleep() 方法来等待某个或某些线程结束。
下面这个例子将创建两个线程,并分别依次输出两个直角三角形。
public class PrintThreadDemo {
public static void main(String[] args) {
PrintThread p = new PrintThread();//创建两个线程
Thread t1 = new Thread(p, "thread1");
Thread t2 = new Thread(p, "thread2");
t1.start();
t2.start();
}
}
class PrintThread implements Runnable {
public void run() {//实现接口的run方法打印直角三角形
System.out.println(Thread.currentThread().getName());
for (int count = 1; count < 50; count++) {
for (int i = 0; i < count; i++) {
System.out.print("*");
}
System.out.println(); //显示完一行以后换行输出
}
}
}
编译并运行这个程序就会发现,实际运行的结果并不一定是两个直角三角形,有可能是一些乱七八糟的“*”号行,有的长,有的短(这取决于操作系统,Java线程在不同的操作系统下运行会有不同的结果)
Java在不进行任何控制的情况下不可能按照调用顺序来执行线程,而是交替执行,主要是因为操作系统在进行进程切换之后,其JVM进程中的各个线程会重新抢占CPU
如果要想得到预期的结果,就需要对这两个线程加以适当的控制,让第一个线程先执行,并判断第一个线程是否已经终止,如果已经终止,再调用第二个线程来执行。解决这个问题的方案是通过调用Thread类的isAlive() 方法和sleep() 方法来实现题目要求
public class SleepThreadDemo {
public static void main(String[] args) {
PrintThread1 p = new PrintThread1();
Thread t1 = new Thread(p, "thread1");
Thread t2 = new Thread(p, "thread2");
t1.start();//执行第一个线程
while (t1.isAlive()) {//不断查询第一个线程的状态
try {
Thread.sleep(100);//让主线程休眠100毫秒,程序不往下执行
} catch (InterruptedException e) {
}
}
t2.start();//直到第一个线程终止,运行第二个线程
}
}
class PrintThread1 implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName());
for (int count = 1; count < 50; count++) {
for (int i = 0; i < count; i++) {
System.out.print("*");
}
System.out.println();//显示完一行以后换行输出
}
}
}
线程的合并
Thread类中的join() 方法也可以用来等待一个线程的结束,而且这个方法更为常用,它的语法格式如下所示:
public final void join() throws InterruptedException
该方法将使得当前线程等待调用该方法的线程结束后,再恢复执行。对前面的示例做一些改动,引入join() 方法,并观察示例运行的结果是否有所变化。改动后的代码如下所示。
public class JoinThreadDemo {
public static void main(String[] args) {
PrintThread2 p = new PrintThread2();
Thread t1 = new Thread(p, "thread1");
Thread t2 = new Thread(p, "thread2");
t1.start();// 执行第一个线程
try {
t1.join();// 当前线程等待线程t1执行完后再继续往下执行
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();// 直到第一个线程终止,运行第二个线程
}
}
class PrintThread2 implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName());
for (int count = 1; count < 50; count++) {
for (int i = 0; i < count; i++) {
System.out.print("*");
}
System.out.println(); // 显示完一行以后换行输出
}
}
}
join() 方法其实很像是将while循环、isAlive() 方法以及sleep() 方法在功能上进行的组合,只不过join()方法将它们给封装了起来。因此,使用该方法等待一个线程结束将使程序更加简练,值得提倡。
线程的中断
首先必须先明确“中断”这个概念的实际含义,这里的中断是指一个线程在其任务完成之前被强行停止,提前消亡的过程。查阅JDK的帮助文档,可以找到这样一个和中断有关的方法:interrupt()
该方法的功能是中断一个线程的执行。但是,在实际使用当中发现,这个方法不一定能够真地中断一个正在运行的线程。下面通过一个例子来看一看使用interrupt() 方法中断一个线程时所出现的结果。示例代码如下所示:
public class InterruptThreadDemo {
public static void main(String[] args) throws InterruptedException {
AThread m = new AThread(); // 创建线程对象m
System.out.println("Starting thread...");
m.start(); // 启动线程m
Thread.sleep(2000); // 主线程休眠2秒,使线程m一直得到执行
System.out.println("Interrupt thread...");
m.interrupt(); // 调用interrupt()方法中断线程m
Thread.sleep(2000); // 主线程休眠2秒,观察中断后的结果
System.out.println("Stopping application..."); // 主线程结束
}
}
class AThread extends Thread {
public void run() {
while (true) {// 无限循环,并使线程每隔1秒输出一次字符串
System.out.println(getName() + " is running");
try {
sleep(1000);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
}
通过对结果的分析,可以发现,用户线程在调用了interrupt() 方法之后并没有被中断,而是继续执行,直到人为的终止程序为止。这个例子说明一个事实,直接使用interrupt()方法并不能中断一个正在运行的线程。那么用什么样的方法才能中断一个正在运行的线程呢?
下面通过在程序中引入共享变量来改进前面的示例,改进后的代码如下所示:
public class InterruptThreadDemo2 {
public static void main(String[] args) throws InterruptedException {
AThread2 m = new AThread2(); // 创建线程对象m
System.out.println("Starting thread...");
m.start(); // 启动线程m
Thread.sleep(2000); // 主线程休眠2秒,使线程m一直得到执行
System.out.println("Interrupt thread...");
m.stop = true; // 修改共享变量
Thread.sleep(2000); // 主线程休眠2秒,观察中断后的结果
System.out.println("Stopping application..."); // 主线程结束
}
}
class AThread2 extends Thread {
boolean stop = false; // 引入一个布尔型的共享变量stop
public void run() {
while (!stop) {// 通过判断stop变量的值来确定是否继续执行线程体
System.out.println(getName() + " is running");
try {
sleep(1000);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
}
线程同步:
什么是同步,如何实现在多线程访问同一资源的时候保持同步呢?
首先分析一个多线程的示例,在这个示例中各线程之间共享同一数据资源,从而模拟出火车站订票系统的处理程序,但是最后程序却出现了意料不到的结果。这个火车站订票系统不同步的模拟程序代码如下
public class NoSynchronizeDemo {
public static void main(String[] args) {
SaleTickets m = new SaleTickets();
Thread t1 = new Thread(m, "System 1");
Thread t2 = new Thread(m, "System 2");
t1.start();
t2.start();
}
}
class SaleTickets implements Runnable {
private String ticketNo = "800800"; // 车票编号
private int ticket = 1; // 共享私有成员,编号为800800的车票数量为1
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is saling Ticket " + ticketNo);//当前系统正在处理订票业务
if (ticket > 0) {
try {// 休眠0-1000毫秒,用来模拟网络延迟
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket = ticket - 1; // 修改车票数据库的信息
// 显示当前该车票的预订情况
System.out.println("ticket's amount is left: " + ticket);
} else {
// 显示该车票已被预订
System.out.println("Sorry,Ticket " + ticketNo + " is saled");
}
}
}
实现同步的方法:
分析运行结果,可以发现同一时间有两个订票系统在处理编号为800800的车票的预订请求,结果由于网络延迟的影响,该车票被预订了两次,使得车票的数量变成了负数。
如何解决这一问题呢?只需要在程序中引入同步机制就可以完全解决这类线程安全问题。
什么是同步呢?当两个或多个线程需要访问同一资源时,它们需要以某种顺序来确保该资源某一时刻只能被一个线程使用的方式称为同步。同步是基于“监视器”这一概念。“监视器”是用作互斥锁的对象。监视器可被视为类似于一个小盒子,它一次只能容纳一个线程。如果一个线程进入监视器,在它退出监视器前,其他所有线程都必须等待。用这种方法,可以防止共享的资源被多个线程操纵。
可以用两种方法同步化代码。两者都使用synchronized关键字,下面分别说明这两种方法。
public class SynchronizeDemo {
public static void main(String[] args) {
SaleTickets2 m = new SaleTickets2();
Thread t1 = new Thread(m, "System 1");
Thread t2 = new Thread(m, "System 2");
t1.start();
t2.start();
}
}
class SaleTickets2 implements Runnable {
private String ticketNo = "800800"; // 车票编号
private int ticket = 1; // 共享私有成员,编号为800800的车票数量为1
public void run() {
System.out.println(Thread.currentThread().getName()+ " is saling Ticket " + ticketNo);
sale();
}
public synchronized void sale() {// 同步方法中的代码为关键代码
if (ticket > 0) {
try {// 休眠0-1000毫秒,用来模拟网络延迟
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket = ticket - 1; // 修改车票数据库的信息
System.out.println("ticket is saled by "
+ Thread.currentThread().getName() + ", amount is: "
+ ticket);
} else {// 显示当前该车票的预订情况
System.out.println("Sorry " + Thread.currentThread().getName()
+ ",Ticket " + ticketNo + " is saled");
}
}
}
public class SynchronizeDemo2 {
public static void main(String[] args) {
SaleTickets3 m = new SaleTickets3();
Thread t1 = new Thread(m, "System 1");
Thread t2 = new Thread(m, "System 2");
t1.start();
t2.start();
}
}
class SaleTickets3 implements Runnable {
private String ticketNo = "800800"; // 车票编号
private int ticket = 1; // 共享私有成员,编号为800800的车票数量为1
public void run() {
System.out.println(Thread.currentThread().getName()+ " is saling Ticket " + ticketNo);
synchronized (this) {
if (ticket > 0) {
try { // 休眠0-1000毫秒,用来模拟网络延迟
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
}
ticket = ticket - 1; // 修改车票数据库的信息
System.out.println("ticket is saled by "
+ Thread.currentThread().getName() + ", amount is: "+ ticket);
} else {// 显示当前该车票的预订情况
System.out.println("Sorry " + Thread.currentThread().getName()
+ ",Ticket " + ticketNo + " is saled");
}
}
}
}
线程死锁
当两个或多个线程等待一个不可能满足的条件时会发生死锁。
死锁是发生在线程间相互阻塞的现象,允许多个线程并发访问共享资源时,必须提供同步机制,然而如果对这种机制使用不当,可能会出现线程永远被阻塞的现象。
例如两个线程分别等待对方各自占有的一个资源,就会产生死锁。
Java本身既不能预防死锁,也不能发现死锁,只能靠程序设计上的谨慎来避免
import static java.lang.System.out;
class Share{
private Share share;
public void setShare(Share share) {
this.share = share;
}
public synchronized void getBook() throws InterruptedException{
out.println("第一个线程得到书!");
Thread.sleep(500);
share.getPictureByFirstThread();
}
public synchronized void getBookBySecondThread() throws InterruptedException{
out.println("第二个线程得到书!");
}
public synchronized void getPicture() throws InterruptedException{
out.println("第二个线程得到画!");
Thread.sleep(500);
share.getBookBySecondThread();
}
public synchronized void getPictureByFirstThread() throws InterruptedException{
out.println("第一个线程得到画!");
}
public void notSynMethod(){
out.println("非同步方法");
}
}
public class MyThread extends Thread{
private Share share;
private boolean firstThread;
public MyThread(Share share,boolean firstThread){
this.share=share;
this.firstThread=firstThread;
}
public void run() {
try {
if(firstThread){
share.getBook();
}else{
share.getPicture();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Share book=new Share();
Share picture=new Share();
book.setShare(picture);
picture.setShare(book);
Thread t1=new MyThread(book,true);
Thread t2=new MyThread(picture,false);
t1.start();t2.start();//启动两个线程
}
}
线程间通信-wait-notify 机制
Java提供了3个非常重要的方法来巧妙地解决线程间的通信问题。这3个方法分别是:wait()、notify() 和notifyAll()。
wait():等待方法。可让当前线程放弃监视器并进入睡眠状态,直到其他线程进入同一监视器并调用notify()方法为止
notify():唤醒方法。可唤醒同一对象监视器中调用了wait()方法的随机线程,并把它移入锁申请队列。
notifyAll():唤醒所有线程的方法。唤醒同一对象监视器中调用了wait()方法的所有线程,具有最高优先级的线程首先被唤醒。
水泵与水池问题就是经典的用来说明线程间通信的问题
一个水池,一个进水泵,一个出水泵;进水泵往水池中注入水,出水泵则将水池中的水抽出去,其中只有当水池中存在水时出水泵才可能工作,同样,也只有当水池在未满状态下,进水泵才能工作,当进出水泵以非稳态流方式运行时则水池中的水随时可能发生溢出和被抽干的情况,这就需要DCS系统调控机制来动态侦测水位情况并实时控制进出水泵的启停状态
这个问题有许多实现方法。下面示例中显示了利用wait-notify 机制解决水泵与水池问题的代码。
class Pool{//水池类
private int maxLevel;//水池最高水位(米)
private int waterLevel;//动态水位高度(米)
public Pool(int maxLevel,int waterLevel){
this.maxLevel=maxLevel;
this.waterLevel=waterLevel;
}
public int getWaterLevel() {
return waterLevel;
}
public synchronized void inWater() throws InterruptedException{
if(waterLevel==maxLevel)this.wait();
waterLevel++;
this.notify();
System.out.println("进水后水位:"+waterLevel);
}
public synchronized void outWater() throws InterruptedException{
if(waterLevel==0)this.wait();
waterLevel--;
this.notify();
System.out.println("出水后水位:"+waterLevel);
}
}
class WaterPump implements Runnable{//水泵类
private Pool pool;//两个水泵共享的水池对象
private boolean isInWaterPump;//是否为进水泵
public WaterPump(Pool pool,boolean isInWaterPump){
this.pool=pool;
this.isInWaterPump=isInWaterPump;
}
public void run() {
try {
if(isInWaterPump){//进水泵进水两百次
for(int i=0;i<200;i++)pool.inWater();
}else{//出水泵出水两百次
for(int i=0;i<200;i++)pool.outWater();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TestSynThread{//测试类
public static void main(String[] args) {
Pool pool=new Pool(50,20);//水池对象,水位初始高度20米,最高不超过50米
WaterPump inWaterPump=new WaterPump(pool,true);//进水泵对象
WaterPump outWaterPump=new WaterPump(pool,false);//出水泵对象
Thread in=new Thread(inWaterPump,"进水泵线程");
Thread out=new Thread(outWaterPump,"出水泵线程");
in.start();//启动进水泵
out.start();//启动出水泵
}
}
守护线程:
守护线程是这样的一种线程:它不需要进程等待它的结束,即:如果一个JVM进程中全部是守护线程时(没有任何用户线程的时候)该JVM进程将直接结束,只要有一个用户线程存在则JVM进程就不会结束
我们熟知的JVM中的垃圾回收机制就是一个守护线程,它在不断的跟踪判断一个对象是否还存在到根级路径(栈或方法区)的引用,如果不存在根级引用则该线程将回收该对象占用的内存空间
一个Java程序在启动之后(即一个JVM进程启动后)至少存在两个线程,一个是主线程main,它是一个用户线程,另外一个是垃圾回收子线程,它是一个守护线程
import static java.lang.System.out;
class DaemonThread extends Thread{
private boolean isFirst;
public DaemonThread(boolean isFirst){
this.isFirst=isFirst;
}
public void run() {
if(isFirst){
for(int i=0;i<100;i++){
out.println("第一个线程:"+i);
}
}else{
for(int i=0;i<100;i++){
out.println("第二个线程:"+i);
}
}
}
}
public class TestDaemonThread{
public static void main(String[] args) {
Thread t1=new DaemonThread(true);
Thread t2=new DaemonThread(false);
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
}
}