這一篇說一下比較枯燥的東西,爲何說枯燥呢,由於我寫這都感受很無聊,無非就是幾個阻塞線程的方法和喚醒線程的方法。。。 java
1.線程中斷 安全
首先咱們說一說怎麼使得一個正在運行中的線程進入阻塞狀態,這也叫作線程中斷,最多見的就是Thread.sleep(1000)這種方式的,咱們直接看一個簡單粗暴的圖:多線程
此圖應該列舉了全部中斷,咱們選擇幾個比較重要的說說,其中有幾個方法已經被廢棄了,緣由要麼就是不安全,要麼就是容易產生死鎖等等,圖中已經劃去了!ide
2.等待通知和收到通知(wait、notify、notifyAll)性能
因爲這wait/notify這種不怎麼好理解,咱們就詳細說說,其餘的隨便看看就好。。。測試
你們不知道有木有發現,java中幾乎全部的數據類型都重寫了Object的wait()、notify()、notifyAll()這三個方法,那這三個方法究竟是幹什麼的呢?spa
前提:要用這三個方法必需要放在synchronized裏面,這是規則!!!至於調用這三個方法必須是鎖定(也就是我前面說的鎖芯)才能調用,還有,鎖定幾乎能夠是任何類型的!線程
· 舉例下面兩種用法:code
介紹一個東西,名字叫作「wait set」,這是個什麼東西呢?你就把這個看做是一個存阻塞線程的集合(大白話來講就是線程的休息室,把線程用wait阻塞了就至關於讓線程去休息室休息,而且線程頗有自覺,去休息了以後就會釋放鎖),並且每一個鎖定(也就是我前面說的鎖芯)都有一個。好比一個線程調用obj.wait()以後,那麼這個線程就被阻塞了,這個阻塞的線程就被放在了obj的「wait set」中了,咱們能夠用一個圖來表示:
blog
當線程A阻塞以後就被丟進了obj的wait set中以後,線程A就會釋放當前的鎖,此時線程B就能夠訪問這個方法或相同鎖定的方法;可是假如在B中調用了notify()方法,那麼就是從obje的wait set中喚醒A線程,而後直到B線程結束後釋放鎖,A線程才變成準備就緒狀態,能夠隨時被CPU調度再次得到這個鎖;
注意:必須等B線程執行完以後釋放鎖,線程A才能變成準備就緒狀態(代碼是從wait方法後面的代碼開始執行,不是從新開始)
根據上面兩個圖隨意舉個小例子:
package com.wyq.thread; public class Bank { Object obj = new Object();//咱們隨便建立一個鎖定 public void tomoney(Integer money){
//在轉帳方法的鎖中調用wait方法,此時執行這個方法的線程會中斷,保存在obj的wait set中,而且該線程會釋放鎖其餘線程能夠訪問相同鎖定的鎖 synchronized(obj){ try { obj.wait(); System.out.println("轉帳:"+money+"元"); } catch (InterruptedException e) { e.printStackTrace(); } } } public void save(Integer money){
//在存錢方法的鎖中咱們調用notify從obj的wait set中喚醒存在其中的某一個線程,那個被喚醒的線程不會立刻變成準備就緒狀態,
//必需要等本存錢方法的線程執行完畢釋放鎖,纔會進入準備就緒狀態 synchronized(obj){ obj.notify(); System.out.println("存錢:"+money+"元"); } } public static void main(String[] args) { Bank bank = new Bank(); //咱們能夠屢次運行這兩個線程,老是先執行存錢方法,而後纔是轉帳方法(其實轉帳線程能夠利用for循環建立幾十個,這樣效果更明顯) new Thread(new Runnable() { @Override public void run() { bank.tomoney(100); } }).start(); new Thread(new Runnable() { @Override public void run() { bank.save(100); } }).start(); } }
運行結果以下:
notify()是喚醒wait set集合中隨意一個線程;而那個notifyAll()方法能夠喚醒wait set集合中全部的線程,用法和notify同樣,就不舉例子了;那麼咱們日常應該用哪個呢?用notify的刷喚醒的線程比較少效率高一點,可是缺點就是到底喚醒哪個線程的實現可能有點難度,一個處理很差程序就可能掛掉了;可是用notifyAll的話效率比較低一點,可是卻比較可靠穩定;
因此啊,若是咱們對於程序代碼都理解得十分透徹,那就用notify比較好,不然仍是用穩妥一點的notifyAll吧!
順便說一點,有的時候咱們把一個線程阻塞以後放進wait set中以後,卻忘記調用notify/notifyAll了,那麼這些阻塞線程就會一直留在了wait set中,咱們能夠在wait()方法指定一個時間,在規定時間內若是沒有被notify喚醒,那麼放在wait set中的該線程就會自動喚醒!還有obj.wait()方法其實本質是調用obj.wait(0),wait(long timeout)是一個Native方法!好比obj.wait(3000)表示三秒以後會自動喚醒!這裏就是隨意提一下,通常不多去指定這個超時時間的
補充wait set的定義:wait set是一個虛擬的概念,它既不是實例的字段,也不是能夠獲取在實例上wait中線程的列表的方法。
3.sleep和interrupt方法
有沒有以爲上面的這種用法比較麻煩,雖然在某些狀況下比較適用,可是咱們日常測試用的話這也太麻煩了,還有個什麼鎖定這種鬼東西,有沒有比較簡單的用法啊!
因而咱們有了sleep方法對應於wait方法,interrupt方法對應於notify方法;(注意,這裏只是功能上面的對應,可是其中的原理是不相同的!)
首先說說sleep方法,這個應該比較熟悉,直接就是Thread.sleep(xxx)這種方式來使得當前線程阻塞必定時間(注意sleep方法和wait方法最大的區別就是sleep方法不會釋放鎖),若是沒有到達相應時間咱們非要讓阻塞狀態的線程又從新變成準備就緒狀態,就使用a.interrupt()方法,其中a指的是當前線程的實例;
咱們看一個最簡單的例子:
package com.wyq.thread; public class Bank { public void tomoney(Integer money){ try { //將運行這個方法的線程中止一個星期 Thread.sleep(604800000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("轉帳:"+money+"元"); } public static void main(String[] args) { Bank bank = new Bank(); Thread thread = new Thread(new Runnable() { @Override public void run() { bank.tomoney(100); } }); thread.start();
//若是沒有調用interrupt方法,那麼thread這個線程就會暫停一個星期 thread.interrupt(); } }
會拋出一個異常這也是用interrupt方法的特點;
隨意一提:這個interrupt方法比較厲害,即便線程中斷是因爲wait(),join(),sleep()形成的,可是均可以用interrupt方法進行喚醒,比較厲害!
4.join()方法
因爲線程的執行是隨機的,那麼咱們有沒有設什麼方法可讓線程以必定的順序執行呢?雖然可能會有點影響性能,但這不是咱們暫時關心的。
join()方法可讓一個線程B在線程A以後才執行,咱們繼續看一個簡單的例子;
package com.wyq.thread; public class Bank { public void tomoney(String name,Integer money){ System.out.println(name+"轉帳:"+money+"元"); } public static void main(String[] args) { Bank bank = new Bank(); Thread A = new Thread(new Runnable() { @Override public void run() { bank.tomoney("A",100); } }); Thread B = new Thread(new Runnable() { @Override public void run() { bank.tomoney("B",200); } }); A.start(); try { //必要要等到A線程執行完畢纔會執行其餘的線程 A.join(); } catch (InterruptedException e) { e.printStackTrace(); } //這個時候查看一下B的線程是NEW,說明B線程還沒調用start()方法, //調用start方法以後就成Runnable狀態了 System.out.println(B.getState()); B.start(); } }
5.線程優先級和yield()方法
其實線程優先級這這種東西比較坑,這是一個機率問題,優先級分爲10級,1級最低,10級最高,咱們通常建立的線程默認5級;可是優先級高的線程不必定先執行,這個優先級的意思就是線程獲取CPU調用的機率比較大,因此這是一個機率問題,基本用法是Thread thread = new Thread(xxx); thread.setPriority(1); thread.start();
那麼yield方法是幹什麼的呢?yield的意思是放手,讓步,投降,在多線程中是對一個正在運行的A線程用這個方法表示該線程放棄CPU的調度,從新回到準備就緒狀態,而後讓CPU去執行和A線程相同優先級的線程,並且有可能又會執行A線程;並且還有可能調用yield方法無效,emmmm......日了狗了!
我表示最這個方法沒有什麼好感,感受很難控制,這個方法是Thread的靜態方法,直接用Thread.yield()直接用便可,我感受我一生都不會用到這個....這個方法就不測試了,有興趣的小夥伴本身查查別的資料吧!
6.總結
不知道你們有沒有以爲多線程這個東西不能隨便用,用好了雖說能夠提升效率,可是用的很差很容易出現不可控制的問題,這讓我有一種錯覺就是引入了多線程以後,又要解決由多線程引發的更多更麻煩的問題,emmm。。。。