首先回顧一下咱們的基礎知識。java
sleep:segmentfault
線程睡眠,不會釋放鎖api
wait:ide
線程等待。釋放鎖。this
notity:spa
喚醒隨機一個當前對象等待的線程,並不會釋放鎖.net
notityAll:線程
喚醒全部當前對象等待的線程,並不會釋放鎖對象
遇到問題:blog
代碼以下:
package com.zhen.ten_chapter.question; /** * @author zhen * @Date 2019/4/18 10:17 */ public class ThreadDemo { public static void main(String[] args) { To10X tos = new To10X(1, 10); tos.start(); int res = tos.getResult(); System.out.println(res); } static class To10X extends Thread { private int point = 1; private int end = 10; private int result = 0; public To10X(int start, int end) { this.point = start; this.end = end; } @Override public synchronized void run() { while (point != end + 1) { result += point++; System.out.println(Thread.currentThread().getName() + "::" + result); } this.notify(); } public synchronized int getResult() { while(result == 0) { try { System.out.println("鎖定前"); this.wait(); System.out.println("鎖定後"); } catch (Exception e) { e.printStackTrace(); } } return result; } } }
程序發現wait老是會被喚醒。
懷疑是否是老是先wait而後再被notity了,因而將notify註釋掉了,但依然被喚醒。
什麼緣由?
我關注點放在了static關鍵字上了,依稀記得static 方法的鎖鎖對象是類,那麼static類裏面的成員方法的鎖對象是否是也是類呢?
固然是我多想了,可是我依然將static 的內部類改成了一個普通內部類,而後用實例化對象去建立的對應對象與執行方法。但是結果依舊是同樣。
我接下來懷疑是主線程的緣由
由於其餘線程都是從主線程上衍生出來的,線程不是很熟練,依稀記得一些所謂守護線程等概念。因而簡單寫了一個Demo類,主線程wait,而後開一個線程獲取到鎖執行到結束,而後發現能wait住主線程
那是什麼緣由呢?
接下來我以爲必定是有什麼東西喚醒了主線程,notity被我註釋掉了,notityAll沒有編寫。查看wait的api,發現它沒有自動喚醒的說法,有一個參數是延遲等待的意思。它明確聲明瞭只能靠notity和notifyAll喚醒。百度大法好。我狂百度,發現有人遇到和我同樣的問題,參考連接: https://blog.csdn.net/nmyangym/article/details/7850882#commentBox 。爲此,我也去看了一下所謂的jdk的Thread類的join源碼,以下:
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
它確實調用了wait,可是調用了wait能保證喚醒嗎?我感受方向沒問題了,繼續針對點去百度
參考連接:
https://segmentfault.com/q/1010000016744022?utm_source=tag-newest
原來,Thread對象在線程結束的時候,會自動調用一次notifyAll語法,線程結束會執行join方法,join的jdk寫法咱們看到過,大牛給出的依據是openJDK中的源碼:
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); static void *java_start(Thread *thread) { ... thread->run(); return 0; } void JavaThread::run() { ... thread_main_inner(); } void JavaThread::thread_main_inner() { ... this->exit(false); delete this; } void JavaThread::exit(bool destroy_vm, ExitType exit_type) { ... // Notify waiters on thread object. This has to be done after exit() is called // on the thread (if the thread is the last thread in a daemon ThreadGroup the // group should have the destroyed bit set before waiters are notified). ensure_join(this); ... } static void ensure_join(JavaThread* thread) { // We do not need to grap the Threads_lock, since we are operating on ourself. Handle threadObj(thread, thread->threadObj()); assert(threadObj.not_null(), "java thread object must exist"); ObjectLocker lock(threadObj, thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); // Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED. java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); // Clear the native thread instance - this makes isAlive return false and allows the join() // to complete once we've done the notify_all below java_lang_Thread::set_thread(threadObj(), NULL); lock.notify_all(thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); }
結論:
儘可能不要用線程對象作同步鎖的鑰匙,線程結束的時候它會自動調用this.notifyAll()