Java多线程系列--join()和yield()和interrupt()理解「建议收藏」

Java (97) 2023-03-27 17:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Java多线程系列--join()和yield()和interrupt()理解「建议收藏」,希望能够帮助你!!!。
Java多线程系列--join()和yield()和interrupt()理解「建议收藏」_https://bianchenghao6.com/blog_Java_第1张

文章目录

    • join()
    • yield()
    • interrupt()

join()

1.理解:线程的合并指的是:将指定的线程加入到当前的线程之中,可以将两个交替执行的线程合并为顺序执行的线程,如果在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
2.作用:让“主线程”等待“子线程”结束之后才能继续运行
3.源码分析

public final void join() throws InterruptedException {
    join(0);
}

public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

通过上述源码我们可以知道join方法的内容实际上调用的是wait().wait()的作用是让“当前线程”等待,而这里的“当前线程”是指当前在CPU上运行的线程。所以,虽然是调用子线程的wait()方法,但是它是通过“主线程”去调用的;所以,休眠的是主线程,而不是“子线程”.
4.join()示例

// JoinTest.java的源码
public class JoinTest{ 

    public static void main(String[] args){ 
        try {
            ThreadA t1 = new ThreadA("t1"); // 新建“线程t1”

            t1.start();                     // 启动“线程t1”
            t1.join();                        // 将“线程t1”加入到“主线程main”中,并且“主线程main()会等待它的完成”
            System.out.printf("%s finish\n", Thread.currentThread().getName()); 
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    } 

    static class ThreadA extends Thread{

        public ThreadA(String name){ 
            super(name); 
        } 
        public void run(){ 
            System.out.printf("%s start\n", this.getName()); 

            // 延时操作
            for(int i=0; i <1000000; i++)
               ;

            System.out.printf("%s finish\n", this.getName()); 
        } 
    } 
}

运行结果:

t1 start
t1 finish
main finish

结果说明:
运行流程如下图
(01) 在“主线程main”中通过 new ThreadA(“t1”) 新建“线程t1”。 接着,通过 t1.start() 启动“线程t1”,并执行t1.join()。
(02) 执行t1.join()之后,“主线程main”会进入“阻塞状态”等待t1运行结束。“子线程t1”结束之后,会唤醒“主线程main”,“主线程”重新获取cpu执行权,继续运行。
Java多线程系列--join()和yield()和interrupt()理解「建议收藏」_https://bianchenghao6.com/blog_Java_第2张
感谢博主:https://www.cnblogs.com/skywang12345/p/3479275.html

yield()

1.理解:暂停当前线程,以便其他线程有机会执行,不过不能指定暂停的时间,并且也不能保证当前线程马上停止。
2.作用

  1. 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。

  2. 用了yield方法后,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)

  3. 通过yield方法来实现两个线程的交替执行。不过请注意:这种交替并不一定能得到保证。

  4. yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会

3.示例

class ThreadA extends Thread{
    public ThreadA(String name){ 
        super(name); 
    } 
    public synchronized void run(){ 
        for(int i=0; i <10; i++){ 
            System.out.printf("%s [%d]:%d\n", this.getName(), this.getPriority(), i); 
            // i整除4时,调用yield
            if (i%4 == 0)
                Thread.yield();
        } 
    } 
} 

public class YieldTest{ 
    public static void main(String[] args){ 
        ThreadA t1 = new ThreadA("t1"); 
        ThreadA t2 = new ThreadA("t2"); 
        t1.start(); 
        t2.start();
    } 
}

执行结果:

t1 [5]:0
t2 [5]:0
t1 [5]:1
t1 [5]:2
t1 [5]:3
t1 [5]:4
t1 [5]:5
t1 [5]:6
t1 [5]:7
t1 [5]:8
t1 [5]:9
t2 [5]:1
t2 [5]:2
t2 [5]:3
t2 [5]:4
t2 [5]:5
t2 [5]:6
t2 [5]:7
t2 [5]:8
t2 [5]:9

“线程t1”在能被4整数的时候,并没有切换到“线程t2”。这表明,yield()虽然可以让线程由“运行状态”进入到“就绪状态”;但是,它不一定会让其它线程获取CPU执行权(即,其它线程进入到“运行状态”),即使这个“其它线程”与当前调用yield()的线程具有相同的优先级。

interrupt()

1.理解:首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。而 Thread.interrupt 的作用其实也不是中断线程,而是「通知线程应该中断了」,具体到底中断还是继续运行,应该由被通知的线程自己处理。

2.作用
具体来说,当对一个线程,调用 interrupt() 时,① 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。仅此而已。② 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。

interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就可以这样做。
① 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
② 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)

3.中断的相关方法

  • public void interrupt() 将调用者线程的中断状态设为true。
  • public boolean isInterrupted() 判断调用者线程的中断状态。
  • public static boolean interrupted 只能通过Thread.interrupted()调用。 它会做两步操作:返回当前线程的中断状态;将当前线程的中断状态设为false;

感谢博主:https://www.zhihu.com/question/41048032/answer/252905837


把几个方法放到一起对比学习,一下子清晰了好多。好爽!

感谢您的阅读~~

发表回复