【Java 并发】线程join() 和 yield()

看了一遍thinking in java 没懂,看了中文版Java高并发的书才了解。我跪着来学习了

join

Thread 的join和sleep一样是一个可中断的方法,如果有其他线程执行了对当前线程的interrupt操作,也会捕获到中断信号,并擦除线程的interrupt标识,Thread的API提供了3种不同的join方法。

1. public final void join() throws InterruptedException
2. public final synchronized void join(long millis, int nanos) throws InterruptedException
3. public final synchronized void join(long millis)throws InterruptedException

join某个线程A,会使当前线程B进入等待,直到线程A结束生命周期,或者到达给定的时间,那么在此期间B线程是出于BLOCKED的,而不是A线程。
举个?

import java.util.stream.IntStream;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static java.util.stream.Collectors.toList;

public class ThreadJoin {
    public static void main(String[] args) throws InterruptedException{
//        1.定义两个线程,并保存在threads中
        List<Thread> threads=IntStream.range(1,3).mapToObj(ThreadJoin::create).collect(toList());
//    2 启动这两个线程  
        threads.forEach(Thread::start);    
//        3.执行这两个线程join方法
        for(Thread thread:threads){
            thread.join();
        }
//        4. main线程循环输出
        for(int i=0; i<10;i++){
            System.out.println(Thread.currentThread().getName()+"#"+i);
            shortSleep();
        }
    }
//构造简单的线程,每个线程简单循环输出
    private static Thread create(int seq){
    return new Thread(()->{
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"#"+i);
            shortSleep();
        }
    },String.valueOf(seq));
    }

    private static void shortSleep() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结合Java8的语法,创建了两个线程,分别启动,并且调用了每个线程的join方法(注意:join方法是被主线程调用的,因此在第一个线程生命周期没有结束时,第二个线程的join不会被执行,但是此时第二个线程已经启动了),运行上面程序,会发现线程一和线程二会交替的输出直到他们结束生命周期,main线程的循环才开始运行。若将注释3的join全部注释,则三个线程交替输出。

  • join会使当前线程永远地等待下去,直到期间被另外的线程中断,或者join的线程执行结束,或者指定的毫秒数到达,当前的线程也会退出阻塞。
    问:如果一个线程已经结束生命周期,那么调用他的join方法的当前线程会被阻塞吗?
    试了下,好像不会被阻塞

yield

yield 方法属于启发式的方法,提醒调度器“我愿意放弃当前的CPU资源”。如果CPU的资源不紧张,则会忽略这种提醒。
调用yield方法会使得当前线程从RUNNING状态切换到RUNNABLE状态,一般不常用此方法

import java.util.stream.IntStream;
public class ThreadYield {
    public static void main(String[] args) {
        IntStream.range(0,2).mapToObj(ThreadYield::create).forEach(Thread::start);
    }
    private static Thread create(int index){
        return new Thread(()->{
//            if(index==0)
//            Thread.yield();
            System.out.println(index);
        });
    }
}

如上程序运行很多次,数字0和1的输出顺序可能不一定,但是当取消注释,顺序将固定成0,1。这是因为当第一个线程获得CPU资源,他会比较谦虚,主动告诉CPU放弃自己的资源,但是yield只是一个hint提示,并不是每次CPU都会满足yield的提示。

yield和sleep的区别

在jdk1.5以前的版本中yield方法事实上调用了sleep(0),但是他们存在本质的区别

  • sleep会导致当前线程暂停指定的时间,没有CPU时间片的消耗。
  • yield只是对CPU调度器的一个提示,如果没有忽略这个提示,他会导致线程上下文切换。
  • sleep会使线程短暂的block。会在给定时间内释放CPU资源。
  • yield会使RUNNING状态的线程进入RUNNABLE状态(如果没被忽略)。
  • sleep几乎百分百文完成给定时间的休眠,而yield的提示并不一定能担保。
  • 一个线程sleep另一个线程interrupt会捕获到中断信号,而yield则不会。
  • sleep不会放弃对monitor锁的所有权
发布了27 篇原创文章 · 获赞 1 · 访问量 682

猜你喜欢

转载自blog.csdn.net/SUKI547/article/details/102731725