Java线程中yield与join方法的区别

    |     2016年4月29日   |   多线程编程   |     0 条评论   |    1615

多线程问题常被面试官问到。即便日常很少写复杂并发,弄清调度和优先级也能增加信心。此前讨论过 wait() 和 sleep() 的区别,这里对比 join() 与 yield()。原文转自 ImportNew。

一、Java 线程调度背景

JVM 必须实现一个基于优先级的调度程序:每个线程有一个正整数优先级,开发者可以改,JVM 不会因为线程已经跑了一段时间就擅自改优先级。约定是操作系统应选择当前最高优先级的 Java 线程运行;更高优先级到来时可以抢占低优先级。这个约定对操作系统并不总是成立,有时仍可能跑低优先级线程。

Java 并不限定必须用时间片,但多数操作系统会。抢占和时间片常被混淆:抢占指高优先级可以压过低优先级;同优先级之间不能互相抢占,通常靠时间片轮转,但那不是 Java 规范的硬性要求。

二、理解线程优先级

尤其对理解 yield() 很重要:

  • 未指定时,线程带普通优先级。
  • 范围 1 到 10:10 最高,1 最低,5 普通。
  • 最高优先级在执行时被给予优先,但不保证 start 后立刻进入运行。
  • 当前正在运行的线程,相对线程池里等待的线程,往往拥有更高或至少不更低的优先级。
  • 由调度程序决定谁真正执行;用 t.setPriority() 设定,最好在 start 之前。
  • 可用常量 MIN_PRIORITY、MAX_PRIORITY、NORM_PRIORITY。

三、yield() 方法

yield 的意思是放手、放弃。调用它等于告诉虚拟机:我乐意让出处理器。这只是暗示,调度器可以忽略。Thread 中定义为:

public static native void yield();
  • 静态 native 方法。
  • 提示把运行机会交给线程池中相同优先级的线程。
  • 不能保证当前线程立刻回到可运行状态。
  • 只从运行转到可运行,不会进入等待或阻塞。

示例:生产者设为最低优先级,消费者最高。不调用 yield 时,通常消费者先全部打完再打生产者;调用 yield 后,两个线程更可能交替打印。

package test.core.threads;
public class YieldExample {
    public static void main(String[] args) {
        Thread producer = new Producer();
        Thread consumer = new Consumer();
        producer.setPriority(Thread.MIN_PRIORITY);
        consumer.setPriority(Thread.MAX_PRIORITY);
        producer.start();
        consumer.start();
    }
}
class Producer extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("I am Producer : Produced Item " + i);
            Thread.yield();
        }
    }
}
class Consumer extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("I am Consumer : Consumed Item " + i);
            Thread.yield();
        }
    }
}

四、join() 方法

在某个线程实例上调用 join(),当前正在运行的线程会阻塞,直到该实例执行结束(“等这条线程死掉”)。也可以设超时,超时后主线程与任务线程重新平等竞争。join 依赖操作系统计时,不要假定它精确等到你指定的毫秒数。和 sleep 一样,join 通过抛出 InterruptedException 响应中断。

public final void join() throws InterruptedException

package test.core.threads;
public class JoinExample {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new Runnable() {
            public void run() {
                System.out.println("First task started");
                System.out.println("Sleeping for 2 seconds");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("First task completed");
            }
        });
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("Second task completed");
            }
        });
        t.start();  // Line 15
        t.join();   // Line 16:等 t 结束再启动 t1
        t1.start();
    }
}
// Output:
// First task started
// Sleeping for 2 seconds
// First task completed
// Second task completed
方法 作用 保证?
yield() 提示让出 CPU 给同优先级线程 不保证,调度器可忽略
join() 当前线程阻塞直到目标线程结束 会等到结束(可设超时)

一句话总结:yield 只是让出 CPU 的暗示,join 是实打实等到另一条线程结束;优先级能影响调度,但不能保证顺序。

转载请注明来源:Java线程中yield与join方法的区别
本文链接地址:https://ai.zhousir.top/?p=1565
回复 取消