【多线程】Java多线程基础(5)- 线程联合与守护线程

1,019 阅读4分钟

线程联合与守护线程

线程联合

什么是线程联合

一个线程A在占有CPU资源期间,可以让其他线程调用join()和本线程联合

一个线程等待另一个线程任务结束再运行自己的任务,因为需要使用联合线程运行结果的值

join()方法

在Java多线程编程中,join()方法是Thread类中的一个方法,用于让一个线程等待另一个线程执行完毕后再继续执行。

具体来说,当一个线程调用另一个线程的join()方法时,它会被阻塞,直到另一个线程执行完毕后才继续执行。例如,假设有两个线程A和B,如果在线程A中调用了线程B的join()方法,那么线程A会被阻塞,直到线程B执行完毕后才继续执行。

下面是join()方法的一些特点:

  1. join()方法可以带有一个参数,表示等待的时间,如果等待时间过长,则该方法会返回。如果不带参数,则该方法会一直等待,直到被阻塞的线程执行完毕。
  2. join()方法会释放CPU资源,因此不会占用CPU时间片。
  3. join()方法可以用于协调多个线程的执行顺序,例如,可以在主线程中调用子线程的join()方法,确保子线程执行完毕后再继续执行主线程。

需要注意的是,如果多个线程之间存在循环依赖,那么使用join()方法可能会导致死锁问题。因此,在使用join()方法时,需要谨慎处理线程之间的依赖关系,避免出现死锁等问题。

总之,join()方法是Java多线程编程中非常重要的方法之一,它可以帮助我们协调多个线程的执行顺序,实现更加复杂的业务逻辑。

纸上得来终觉浅,因此举例来验证

案例

小明给小红打招呼, 小明说了hello, 在收到小红的答复后, 小明要给小红说今天很开心。

class Ming implements Runnable {
    @Override
    public void run() {
        System.out.println("hello , xiao hong!");
        // 这里使用小红线程
        Thread hong = new Thread(new Hong());
        hong.start();
        try {
            // 等待小红做出回应
            hong.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("我今天很开心");
    }

}
class Hong implements Runnable{
    @Override
    public void run(){
        try {
            // 收到消息用了2s
            Thread.sleep(2000);
            System.out.println("hello,xiao ming!!");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

守护线程

什么是守护线程

守护线程(Daemon Thread)是指在程序运行时在后台提供一种常驻的服务,它的作用是为其他线程提供支持和服务。当所有的非守护线程都结束时,守护线程也会随之结束。

为什么需要守护线程

Java程序入口就是由JVM启动main线程,main线程又可以启动其他线程。当所有线程都运行结束时,JVM退出,进程结束。

如果有一个线程没有退出,JVM进程就不会退出。所以,必须保证所有线程都能及时结束。

但是有一种线程的目的就是无限循环,例如,一个定时触发任务的线程。

但是有了这个无限循环的线程,我的JVM进程永远不会退出, 这显然不是我要的结果。

为此,我们将不会退出的线程设为守护线程, 这样,当非守护线程结束,主程序就会退出。

守护线程的特点

守护线程的特点如下:

  1. 守护线程是在后台运行的,它不会影响程序的主要逻辑,而只是在后台提供一些支持和服务。
  2. 守护线程的生命周期与程序的生命周期相同,当程序结束时,守护线程也会随之结束。
  3. 守护线程通常是一些比较简单的任务,如垃圾回收、日志输出等等。

案例

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Daemon());
        thread.start();
    }
}

class Daemon implements Runnable {
    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(LocalDateTime.now());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

上面的例子,放进编译器运行,编译器永远不会停止。

但当我在调用start()方法前,调用setDaemon(true)把该线程标记为守护线程时,又会发生什么呢?

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Daemon());
        // 设为守护线程
        thread.setDaemon(true);
        thread.start();
        // 为了效果明显,使主线程休息一会儿。
        Thread.sleep(2000);
    }
}

class Daemon implements Runnable {
    @Override
    public void run() {
        while (true){
            try {
                System.out.println(LocalDateTime.now());
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

运行两秒后,主线程结束,守护线程也随之死亡。

守护线程是独立于其它线程的,它退出只有两种情况:

  1. 自身任务执行完毕
  2. 主线程结束