• 多线程join()和yield()源码解析


    join()的作用

    other.join()把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

    在一个线程中调用 other.join() ,这时候当前线程会让出执行权给 other 线程,直到
    other 线程执行完或者过了超时时间之后再继续执行当前线程,join() 源码如下:

    public final synchronized void join(long millis)
    
    throws InterruptedException {
    
        long base = System.currentTimeMillis();
    
        long now = 0;
    
        // 超时时间不能小于 0
    
        if (millis < 0) {
    
            throw new IllegalArgumentException("timeout value is negative");
    
        }
    
        // 等于 0 表示无限等待,直到线程执行完为之
    
        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() 方法来实现的。

    例如,在未使用 join() 时,代码如下:

    public class ThreadExample {
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread thread = new Thread(() -> {
    
                for (int i = 1; i < 6; i++) {
    
                    try {
    
                        Thread.sleep(1000);
    
                    } catch (InterruptedException e) {
    
                        e.printStackTrace();
    
                    }
    
                    System.out.println("子线程睡眠:" + i + "秒。");
    
                }
    
            });
    
            thread.start(); // 开启线程
    
            // 主线程执行
    
            for (int i = 1; i < 4; i++) {
    
                try {
    
                    Thread.sleep(1000);
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }
    
                System.out.println("主线程睡眠:" + i + "秒。");
    
            }
    
        }
    
    }
    

    程序执行结果为:

    主线程睡眠:1秒。
    
    子线程睡眠:1秒。
    
    主线程睡眠:2秒。
    
    子线程睡眠:2秒。
    
    主线程睡眠:3秒。
    
    子线程睡眠:3秒。
    
    子线程睡眠:4秒。
    
    子线程睡眠:5秒。
    
    从结果可以看出,在未使用 join() 时主子线程会交替执行。
    
    然后我们再把 join() 方法加入到代码中,代码如下:
    
    public class ThreadExample {
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread thread = new Thread(() -> {
    
                for (int i = 1; i < 6; i++) {
    
                    try {
    
                        Thread.sleep(1000);
    
                    } catch (InterruptedException e) {
    
                        e.printStackTrace();
    
                    }
    
                    System.out.println("子线程睡眠:" + i + "秒。");
    
                }
    
            });
    
            thread.start(); // 开启线程
    
            thread.join(2000); // 等待子线程先执行 2 秒钟
    
            // 主线程执行
    
            for (int i = 1; i < 4; i++) {
    
                try {
    
                    Thread.sleep(1000);
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }
    
                System.out.println("主线程睡眠:" + i + "秒。");
    
            }
    
        }
    
    }
    

    程序执行结果为:

    复制

    子线程睡眠:1秒。
    
    子线程睡眠:2秒。
    
    主线程睡眠:1秒。 
    
    // thread.join(2000); 等待 2 秒之后,主线程和子线程再交替执行
    
    子线程睡眠:3秒。
    
    主线程睡眠:2秒。
    
    子线程睡眠:4秒。
    
    子线程睡眠:5秒。
    
    主线程睡眠:3秒。
    

    从执行结果可以看出,添加 join() 方法之后,主线程会先等子线程执行 2
    秒之后才继续执行。

    yield()的作用

    看 Thread 的源码可以知道 yield() 为本地方法,也就是说 yield() 是由 C 或 C++ 实现的,源码如下:

    复制
    public static native void yield();
    yield() 方法表示给线程调度器一个当前线程愿意出让 CPU 使用权的暗示,但是线程调度器可能会忽略这个暗示。

    比如我们执行这段包含了 yield() 方法的代码,如下所示:

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("线程:" +
                            Thread.currentThread().getName() + " I:" + i);
                    if (i == 5) {
                        Thread.yield();
                    }
                }
            }
        };
        Thread t1 = new Thread(runnable, "T1");
        Thread t2 = new Thread(runnable, "T2");
        t1.start();
        t2.start();
    }
    

    当我们把这段代码执行多次之后会发现,每次执行的结果都不相同,这是因为 yield() 执行非常不稳定,线程调度器不一定会采纳 yield() 出让 CPU 使用权的建议,从而导致了这样的结果。

  • 相关阅读:
    Binary Tree Maximum Path Sum
    ZigZag Conversion
    Longest Common Prefix
    Reverse Linked List II
    Populating Next Right Pointers in Each Node
    Populating Next Right Pointers in Each Node II
    Rotate List
    Path Sum II
    [Leetcode]-- Gray Code
    Subsets II
  • 原文地址:https://www.cnblogs.com/xiaodou00/p/13499545.html
Copyright © 2020-2023  润新知