zoukankan      html  css  js  c++  java
  • 多线程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 使用权的建议,从而导致了这样的结果。

  • 相关阅读:
    项目开发管理SDEM
    google安装文件 和 缓存位置:
    数据库 查询超时 运行超时 问题
    将Access、Excel数据导出到MSSQL/使用 OpenRowSet 和 OpenDataSource 访问 Excel 97-2007
    Mybatis日志(七)
    Mybatis拦截器(六)
    Mybatis动态SQL(五)
    Mybatis的XML映射文件(四)
    Mybatis全局配置文件详解(三)
    Mybatis环境搭建(二)
  • 原文地址:https://www.cnblogs.com/xiaodou00/p/13499545.html
Copyright © 2011-2022 走看看