1.线程的插队:join()方法

1.1.作用

join() 是多线程编程中的一个重要方法,用于控制线程执行顺序。并不影响同一时刻处于运行状态的其他线程。

package Day02_Thread的方法;

//插队线程:插队对象。join()
public class Demo01 {
    public static void main(String[] args) {
        System.out.println("main线程开始执行...");

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c = 'A'; c < 'Z'; c++) {
                    System.out.printf("[%s]->%s\n",Thread.currentThread().getName(),c);
                }
            }
        });
        t1.setName("线程A");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c = 'a'; c < 'z'; c++) {
                    System.out.printf("[%s]->%s\n",Thread.currentThread().getName(),c);
                }
            }
        });
        t2.setName("线程B");

        t1.start();
        t2.start();

        //线程插队
        try {
            t1.join();//t1线程插队到main线程之前(t1线程执行结束后,才会继续执行main线程)
            t2.join();//t2线程插队到main线程之前(t2线程执行结束后,才会继续执行main线程)
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("main线程结束...");
    }
}

执行顺序:

1.主线程启动子线程t1,t2

2.主线程调用t1和t2的join()方法后,主线程进入阻塞状态,要等t1,t2分别执行完再继续进行主线程。

PS:谁调用join()方法,谁被阻塞!上示例中主线程调用,则是主线程被t1和t2插队

3.主线程执行输出

1.2重载形式

1.join():无限等待,知道目标线程终止

2..join(long millis):等待指定毫秒数,若是超时则不等待。

public final synchronized void join(long millis)

3. join(long millis,int nanos):精确到纳秒的等待(实际精度取决于OS(操作系统)

public final synchronized void join(long millis, int nanos)

1.3 join()和sleep()的区别 

两个方法都可以实现类似"线程等待”的效果,但是仍然存在区别;
        join()执行过程中,会释放当前线程持有的锁;sleep()休眠没有结束前,不会释放锁;

        join()是通过在内部使用 wait/notify 机制来实现;sleep()通过操作系统的定时器( Timer )实现精确休眠,到期后线程重新进入就绪状态;

        join()主要用于实现线程任务编排;sleep()用于模拟耗时操作;

2.线程的中断:interrupt()方法

如果线程需要执行一个长时间任务,就可能需要能中断线程。中断线程就是其他线程给该线程发一个信号,该线程收到信号后,结束执行run()方法使当前线程能立刻结束。

中断一个线程非常简单,只需要在其他线程中对目标线程调用 interrupt()方法,目标线程需要反复检测自身状态是否是 interrupted 状态,如果是,就立刻结束运行。

2.1作用

        interrupt()方法的作用是设置该线程的中断状态为 true ,线程是死亡还是等待新的任务或是继续运行至下一步,就取决于中断状态。线程会不时地检测这个中断状态值,以判断线程是否应该被中断(中断状态值是否为 true )。

2.2实现原理

情况1:线程目前执行的方法或状态,支持中断,实现InterrupteException的方法或者状态

package Day02_Thread的方法;

import java.util.UUID;

public class Demo02 {
    public static void main(String[] args) {
        Thread thread=new Thread(){
            public void run(){
                System.out.println("子线程开始执行,进入Runable状态");

                //情况1:线程目前执行的方法或状态,支持中断
                //实现InterrupteException的方法或者状态
                try {
                    Thread.sleep(1000*6);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("子线程被中断");
                    return;
                }
         
        //子线程启动
        thread.start();

        //main主线程休眠3秒
        try {
            Thread.sleep(1000*3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //主线程中,子线程thread中断,设置中断标志为true
        thread.interrupt();
    }

}

2.情况2:在线程逻辑中,判断中断标志(是否已经设置为中断true)

package Day02_Thread的方法;

import java.util.UUID;

public class Demo02 {
    public static void main(String[] args) {
        Thread thread=new Thread(){
            public void run(){
                System.out.println("子线程开始执行,进入Runable状态");

                //情况1:线程目前执行的方法或状态,支持中断
                //实现InterrupteException的方法或者状态
//                try {
//                    Thread.sleep(1000*6);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                    System.out.println("子线程被中断");
//                    return;
//                }
                //情况2:在线程逻辑中,判断中断标志(是否已经设置为中断true)
                while(!isInterrupted()){
                    System.out.println(UUID.randomUUID().toString());
                }
                System.out.println("子线程结束执行(通过中断结束),进入TERMINATED状态");
            }
        };
        //子线程启动
        thread.start();

        //main主线程休眠3秒
        try {
            Thread.sleep(1000*3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //主线程中,子线程thread中断,设置中断标志为true
        thread.interrupt();
    }

}

总结:

        interrupt()方法只是改变中断状态,不会像 stop()中断一个正在运行的线程。
        支持线程中断的方法(Thread.sleep()、join()、wait()等方法)在执行过程中,会监视线程的中断状态,一旦发现线程的中断状态值被置为“tru,就会抛出线程中断异常InterruptedException,给WAITING或者 TIMEpD WAITING 等待状态的线程发出一个中断信号,从而让当前线程识别到中断标识,就会退出 WAITING或者TIMED WAITING 等待状态;

3.线程的让出:yield()方法

线程通过调用yield()方法告诉JVM的线程调度,当前线程愿意让出CPU给其他线程使用

3.1yield()方法的作用()

线程通过调用 yield()方法告诉 JVM 的线程调度,当前线程愿意让出CPU 给其他线程使用。
至于系统是否采纳,取决于 JVM 的线程调度模型:分时调度模型抢占式调度模型
        分时调度模型:所有的线程轮流获得cpu 的使用权,并且平均分配每个线程占用的 CPU 时间片;
        抢占式调度模型:优先让可运行池中优先级高的线程占用CPU,如果可运
行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。(JVM 虚拟机采用的是抢占式调度模型)

package 线程的方法;
//线程的让出:线程通过调用yield()方法告诉JVM的线程调度,当前线程愿意让出CPU给其他线程使用
public class Demo05 {
    public static void main(String[] args) {
        //创建普通的用户线程
        Thread t1=new Thread("线程"){
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    System.out.println(getName()+":"+i);
                }
            }
        };

        //创建守护线程
        Thread t2=new Thread("线程2"){
            @Override
            public void run() {
                for (char i = 'A'; i < 'Z'; i++) {
                    System.out.println(getName()+":"+i);
                    Thread.yield();
                }
            }
        };

        t1.start();
        t2.start();
    }
}

注意事项:

  • yield() 只是一个提示,不是强制命令

  • 不能保证其他线程一定会获得执行机会

3.2 yield()sleep()join() 和 wait()的对比

特性 yield() sleep() join() wait()
所属类 Thread Thread Thread Object
作用对象 当前线程 当前线程 其他线程 当前线程
是否释放锁 ❌ 不释放 ❌ 不释放 ✅ 释放(底层调用wait() ✅ 释放
唤醒条件 立刻进入就绪状态 时间结束或被中断 目标线程结束或超时或被中断 notify()/notifyAll()或被中断
是否静态方法 ✅ 是 ✅ 是 ❌ 否 ❌ 否
参数要求 无参数 必须指定时间(毫秒/纳秒) 可指定超时时间(可选) 可指定超时时间(可选)
主要用途 让出CPU给同级/更高优先级线程 延迟执行 等待其他线程完成 线程间通信(需配合synchronized
是否抛出异常 ❌ 不抛异常 ✅ 抛出InterruptedException ✅ 抛出InterruptedException ✅ 抛出InterruptedException
示例代码 Thread.yield(); Thread.sleep(1000); thread.join(500); obj.wait(1000);
适用场景 长时间循环中避免独占CPU 定时任务、模拟耗时操作 线程结果依赖或顺序控制 生产者-消费者模型等协作场景

4.守护线程,俗称:舔狗线程(Daemon Thread)

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

用户线程:我们平常创建的普通线程;
守护线程:用来服务于用户线程的线程,在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出;而守护线程执行结束后,虚拟机不会自动退出。

4.2守护线程的设置

在调用start()方法之前,调用setDaemon(true)方法把该方法标记为守护线程

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐