多线程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() 方法的代码,如下所示:

扫描二维码关注公众号,回复: 11622339 查看本文章
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 使用权的建议,从而导致了这样的结果。

推荐阅读:https://docs.qq.com/doc/DVEx4dFRtV1pYeVR0

猜你喜欢

转载自blog.csdn.net/Java0258/article/details/108014010