java代碼:html
package myTest; public class TestThread extends Thread{ private Integer ticketNum = 10; public void run() { synchronized (this) {//這個同步代碼塊和下面的while調換位置會有徹底不一樣的結果 while(true) { if(this.ticketNum > 0) { try { System.out.println(Thread.currentThread().getName()+":"+this.ticketNum); this.ticketNum --; Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } }else{ break; } } } } public static void main(String[] args) { TestThread t = new TestThread();//這麼寫才能保證鎖住的this是同一個實例對象 new Thread(t, "t1").start(); new Thread(t, "t2").start(); /* 下面這種寫法,每一個線程都是不一樣的一個新的ThreadTest的實例對象,鎖住的this分別是不一樣的對象,因此互不阻塞 ThreadTest t = new ThreadTest(); t.setName("t1"); t.start(); ThreadTest t2 = new ThreadTest(); t2.setName("t2"); t2.start(); ThreadTest t3 = new ThreadTest(); t3.setName("t3"); t3.start(); */ } }
解析:java
(1)寫在while外面的時候:t1,t2任意一個線程先拿到執行權的話就會一直是它在循環了。由於沒有能停下來的語句,別的線程也執行不了,由於synchronized(){}是鎖的。只有該線程循環完出來的時候,synchronized(){}纔是打開的。這時候就不能知足t1,t2隨機進入while循環;此時synchronized(){}鎖住的是整個代碼塊,而同步代碼中的sleep()是不會釋放鎖的,會繼續執行sleep()後面的代碼,繼續執行因此t1和t2誰拿到鎖就會一直執行,直到while跳出(若是改成this.wait(),則會釋放鎖,此時t1,t2誰拿到鎖,誰就會進入同步代碼塊)安全
(2)若是寫在while裏面就不一樣了,這二個線程不管是誰獲得cpu執行權都會先進入while循環,而後判斷synchronized(){}是不是鎖的,若是不是就能夠賣出一張票,若是是就等待別的線程先執行完。例如t2拿到鎖,正在執行同步代碼塊,此時t1進入while裏面,由於t2拿到鎖,因此t1要等待t2執行完,當t2執行完同步代碼塊以後,會再次進入while的下一個循環,此時和t1和t2會競爭鎖,拿到鎖的會進入同步代碼塊,此時不會出現只有一個窗口賣票的狀況,而是都在賣票...多線程
(3)簡單拿模擬窗口賣票來講,咱們看到的執行效果的區別就是:
A:鎖在while外面的時候,運行程序,無論有多少個窗口也老是一個窗口在賣票,從頭賣到尾絕對永遠都是先進入循環的那個線程會把全部票賣完this
B:鎖在while循環裏面的時候,會是隨機的一個窗口在賣,若是是二個線程的話,就應該是二個窗口都有在賣。線程
java多線程何時釋放鎖—wait()、notify()code
https://www.cnblogs.com/hy928302776/p/3255641.htmlhtm
因爲等待一個鎖定線程只有在得到這把鎖以後,才能恢復運行,因此讓持有鎖的線程在不須要鎖的時候及時釋放鎖是很重要的。對象
在如下狀況下,持有鎖的線程會釋放鎖:
1. 執行完同步代碼塊。
2. 在執行同步代碼塊的過程當中,遇到異常而致使線程終止。
3. 在執行同步代碼塊的過程當中,執行了鎖所屬對象的wait()方法,這個線程會釋放鎖,進行對象的等待池。
blog
除了以上狀況外,只要持有鎖的此案吃尚未執行完同步代碼塊,就不會釋放鎖。
所以在如下狀況下,線程不會釋放鎖:
1. 在執行同步代碼塊的過程當中,執行了Thread.sleep()方法,當前線程放棄CPU,開始睡眠,在睡眠中不會釋放鎖。該方法調用時,能夠看到線程明顯有被阻塞。可是設定一過,就本身開始繼續執行。能夠說明sleep方法會讓線程阻塞,可是並無放棄本身所持有的鎖,阻塞完畢後,它任然持有鎖,能夠接着執行以前阻塞以後的任務。
2. 在執行同步代碼塊的過程當中,執行了Thread.yield()方法,當前線程放棄CPU,但不會釋放鎖。
3. 在執行同步代碼塊的過程當中,其餘線程執行了當前對象的suspend()方法,當前線程被暫停,但不會釋放鎖。但Thread類的suspend()方法已經被廢棄。
避免死鎖的一個通用的經驗法則是:當幾個線程都要訪問共享資源A、B和C時,保證使每一個線程都按照一樣的順序去訪問他們,好比都先訪問A,再訪問B和C。
java.lang.Object類中提供了兩個用於線程通訊的方法:wait()和notify()。須要注意到是,wait()方法必須放在一個循環中,由於在多線程環境中,共享對象的狀態隨時可能改變。當一個在對象等待池中的線程被喚醒後,並不必定當即恢復運行,等到這個線程得到了鎖及CPU才能繼續運行,又可能此時對象的狀態已經發生了變化。
# 調用obj的wait(), notify()方法前,必須得到obj鎖,也就是必須寫在synchronized(obj) {...} 代碼段內。
# 調用obj.wait()後,線程A就釋放了obj的鎖,不然線程B沒法得到obj鎖,也就沒法在synchronized(obj) {...} 代碼段內喚醒A。
# 當obj.wait()方法返回後,線程A須要再次得到obj鎖,才能繼續執行。
# 若是A1,A2,A3都在obj.wait(),則B調用obj.notify()只能喚醒A1,A2,A3中的一個(具體哪個由JVM決定)。
# obj.notifyAll()則能所有喚醒A1,A2,A3,可是要繼續執行obj.wait()的下一條語句,必須得到obj鎖,
所以,A1,A2,A3只有一個有機會得到鎖繼續執行,例如A1,其他的須要等待A1釋放obj鎖以後才能繼續執行。 # 當B調用obj.notify/notifyAll的時候,B正持有obj鎖,所以,A1,A2,A3雖被喚醒,可是仍沒法得到obj鎖。直到B退出synchronized塊,釋放obj鎖後,A1,A2,A3中的一個纔有機會得到鎖繼續執行。 wait()/sleep()的區別 前面講了wait/notify機制,Thread還有一個sleep()靜態方法,它也能使線程暫停一段時間。sleep與wait的不一樣點是:sleep並不釋放鎖,而且sleep的暫停和wait暫停是不同的。obj.wait會使線程進入obj對象的等待集合中並等待喚醒。 可是wait()和sleep()均可以經過interrupt()方法打斷線程的暫停狀態,從而使線程馬上拋出InterruptedException。 若是線程A但願當即結束線程B,則能夠對線程B對應的Thread實例調用interrupt方法。若是此刻線程B正在wait/sleep/join,則線程B會馬上拋出InterruptedException,在catch() {} 中直接return便可安全地結束線程。 須要注意的是,InterruptedException是線程本身從內部拋出的,並非interrupt()方法拋出的。對某一線程調用interrupt()時,若是該線程正在執行普通的代碼,那麼該線程根本就不會拋出InterruptedException。可是,一旦該線程進入到wait()/sleep()/join()後,就會馬上拋出InterruptedException。