【線程】 Thread.sleep 與 Thread.yield 的區別 (五)

我的原則:先會用再說,內部慢慢來


Thread.sleep 與 Thread.yield 的區別

一、 作用

  1. Thread.sleep 的作用是啥:當前thread睡覺,讓出資源。(不釋放鎖)
  2. Thread.yield 作用是啥:當前 thread睡覺,讓出資源。(不釋放鎖)

二、注意點

  1. 記住鎖是鎖對象,不是鎖住線程

三、區別

  1. Thread.sleep 有一個方法: Thread.sleep(long millis),需要傳遞參數。 millis > 0 的時候,可以被打斷。
  2. Thread.yield 有一個方法: Thread.yield() ,不需要傳遞參數。睡覺過程不允許被打斷。
  3. Thread.sleep(0) = Thread.yield() 【效果一樣】,兩者的意思都是不釋放鎖的前提下,讓出資源,等到別的線程搞完再來弄我自己的。

四、代碼

public class _05_01_YieldTest implements Runnable {
    @Override
    public void run(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread() + ": " + i);
//            Thread.yield();
            try {
                Thread.sleep(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        _05_01_YieldTest runn = new _05_01_YieldTest();
        Thread t1 = new Thread(runn, "t1");
        Thread t2 = new Thread(runn, "t2");

        t1.start();
        t2.start();
        Thread.sleep(100);
        for (int i = 0; i < 3; i++) {
            System.out.println("Main " + Thread.currentThread() + ": " + i);
            //            Thread.yield();
            try {
                Thread.sleep(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

輸出:

Thread[t1,5,main]: 0
Main Thread[main,5,main]: 0
Thread[t2,5,main]: 0
Main Thread[main,5,main]: 1
Thread[t1,5,main]: 1
Main Thread[main,5,main]: 2
Thread[t2,5,main]: 1
Thread[t1,5,main]: 2
Thread[t2,5,main]: 2

很明顯, Thread.sleep(0) 效果跟Thread.yield()效果都是一樣的。但是如果 Thread.sleep(long millis)的參數 millis大於0,那麼JVM底層調用的是 os::sleep,等於0調用的是 os::yield();

五、 JVM源碼

jvm.cpp 源碼查看

  1. 先看 Thread.Yeild 底層
JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass))
  JVMWrapper("JVM_Yield");
  //檢查是否設置了DontYieldALot參數,默認爲fasle
  //如果設置爲true,直接返回
  if (os::dont_yield()) return;
 //如果ConvertYieldToSleep=true(默認爲false),調用os::sleep,否則調用os::yield
  if (ConvertYieldToSleep) {
    os::sleep(thread, MinSleepInterval, false);//sleep 1ms
  } else {
    os::yield();
  }
JVM_END

所以實際上調用的是 os::yield()

//sched_yield是linux kernel提供的API,它會使調用線程放棄CPU使用權,加入到同等優先級隊列的末尾;
//如果調用線程是優先級最高的唯一線程,yield方法返回後,調用線程會繼續運行;
//因此可以知道,對於和調用線程相同或更高優先級的線程來說,yield方法會給予了它們一次運行的機會;
void os::yield() {
  sched_yield();
}
  1. 再看 Thread.sleep 底層
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
  JVMWrapper("JVM_Sleep");

  if (millis < 0) {//參數校驗
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }

  //如果線程已經中斷,拋出中斷異常,關於中斷的實現,在另一篇文章中會講解
  if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
    THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
  }
 //設置線程狀態爲SLEEPING
  JavaThreadSleepState jtss(thread);

  EventThreadSleep event;

  if (millis == 0) {
    //如果設置了ConvertSleepToYield(默認爲true),和yield效果相同
    if (ConvertSleepToYield) {
      os::yield();    // ====================  這裏是重點 !!!!!====================
    } else {//否則調用os::sleep方法
      ThreadState old_state = thread->osthread()->get_state();
      thread->osthread()->set_state(SLEEPING);
      os::sleep(thread, MinSleepInterval, false);//sleep 1ms
      thread->osthread()->set_state(old_state);
    }
  } else {//參數大於0
   //保存初始狀態,返回時恢復原狀態
    ThreadState old_state = thread->osthread()->get_state();
    //osthread->thread status mapping:
    // NEW->NEW
    //RUNNABLE->RUNNABLE
    //BLOCKED_ON_MONITOR_ENTER->BLOCKED
    //IN_OBJECT_WAIT,PARKED->WAITING
    //SLEEPING,IN_OBJECT_WAIT_TIMED,PARKED_TIMED->TIMED_WAITING
    //TERMINATED->TERMINATED
    thread->osthread()->set_state(SLEEPING);
    //調用os::sleep方法,如果發生中斷,拋出異常
    if (os::sleep(thread, millis, true) == OS_INTRPT) {
      if (!HAS_PENDING_EXCEPTION) {
        if (event.should_commit()) {
          event.set_time(millis);
          event.commit();
        }
        THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
      }
    }
    thread->osthread()->set_state(old_state);//恢復osThread狀態
  }
  if (event.should_commit()) {
    event.set_time(millis);
    event.commit();
  }
JVM_END

看到沒,millis = 0 的時候, sleep 底層就是 yield。

六、 番外篇

下一章節:【線程】可重入鎖與不可重入鎖(六)
上一章節:【線程】 Thread.yeild 內部原理 (四)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章