多線程技術分享
背景:
以前單CPU,單任務的條件下,一段時間只能執行單一的程序。之后發展到多任務階段。多個任務共享一個CPU,本質上是有操作系統來完成CPU對多個用戶的切換。保證每個任務都有一定的時間片來完成任務。
1.????? 概念和原理
?
進程是指一個內存中運行的應用程序,每個進程都有自己獨立的一塊內存空間,一個進程中可以啟動多個線程。
?
線程是指進程中的一個執行流程,一個進程中可以運行多個線程。線程總是屬于某個進程,進程中的多個線程共享進程的內存。
2.????? 線程的兩種實現方法
1、擴展java.lang.Thread類。
?
此類中有個run()方法,應該注意其用法:
public void run()
如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;否則,該方法不執行任何操作并返回。
Thread 的子類應該重寫該方法。
2、實現java.lang.Runnable接口。
?
void run()
使用實現接口 Runnable 的對象創建一個線程時,啟動該線程將導致在獨立執行的線程中調用對象的 run 方法。
方法 run 的常規協定是,它可能執行任何所需的操作。
?
兩種方式的區別:既然都能實現線程的功能,怎樣區別使用呢。java規定只能單繼承,如果自定義類需要繼承其他類,只能選擇實現Runnable接口。
3. 線程的理解
線程繼承Thread 或者實現Runnable接口。既然是線程,就必然有寫的run方法,其次,多線程是多個線程處理公共的資源,所以,每個線程都共享公用資源,所以每個線程里面都有同一個資源對象。通常,公共資源有線程的構造方法傳遞的。如下,共享資源pendingIfData。
/**??*?接口調用線程??*???*?@author?maoping??*??*/??public?class?ThreadIfExcuse?extends?Thread?{????????//?線程名稱??10. ????private?String?threadName;??11. ??12. ????//?待處理接口數據公共類??13. ????private?PendingIfData?pendingIfData;??14. ??15. ????ThreadIfExcuse(String?threadName,?PendingIfData?pendingIfData)?{??16. ????????this.threadName?=?threadName;??17. ????????this.pendingIfData?=?pendingIfData;??18. ??19. ????}??20. ??21. ????@Override??22. ????public?void?run()?{??23. ????????while?(true)?{??24. ????????????pendingIfData.ifExcuse(threadName);??25. ????????}??26. ??27. ????}??28. ??29. }??
詳細描述共享資源的內容。實現互斥操作的方式有多種,實例中使用await / signal來控制。一般而言,公用共享資源類中包含
?
數據共享的存儲介質,需要線程安全,本示例中為LinkedBlockingQueue類型的interfaceUUIDQueue 。
?
互斥執行方法fExcuse (進入方法使用lock將代碼鎖住,使用條件condition的await,讓代碼線程處于等待狀態。然后是從數據共享的存儲介質中獲取待處理數據,然后執行互斥的業務方法。最后lock解鎖(lock.unlock)。
?
喚醒方法:如果是一個類調用喚醒方法,則是簡單的多線程處理,如果是多個線程調用喚醒方法,則為生產者,消費者,倉庫模型了。喚醒方法的詳情。首先lock鎖定,其次給共享的存儲介質中加入待處理數據單元,然后喚醒添加下等待的線程同時醒來進行一次控制權搶奪。其中一個獲取控制權去處理一個數據。
?
/**??*?待處理接口數據線程??*???*?@author?maoping??*??*/??public?class?PendingIfData?{????????private?ReentrantLock?lock?=?new?ReentrantLock();??10. ??11. ????private?Condition?condition?=?lock.newCondition();??12. ??13. ????private?LinkedBlockingQueue<String>?interfaceUUIDQueue?=?new?LinkedBlockingQueue<String>();??14. ??15. ????//?condition?必須在lock鎖范圍內??16. ????public?void?ifExcuse(String?threadName)?{??17. ??18. ????????try?{??19. ????????????lock.lock();??20. ????????????condition.await();??21. ????????????//?接口調用??22. ????????????String?IfUUID?=?interfaceUUIDQueue.take();??23. ????????????//?靜態類?不能注入服務?需要使用getBean方式回去服務類??24. ????????????IfExcuseService?ifExcuseService?=?new?IfExcuseService();??25. ????????????ifExcuseService.ifExcuse(IfUUID);??26. ????????}?catch?(InterruptedException?e)?{??27. ????????????//?TODO?Auto-generated?catch?block??28. ????????????e.printStackTrace();??29. ????????}?finally?{??30. ????????????lock.unlock();??31. ????????}??32. ??33. ????}??34. ??35. ????/**?36. ?????*?添加待調用的線程UUID?并激活等待線程?37. ?????*??38. ?????*?@param?IfUUID?39. ?????*/??40. ????public?void?putIfUUID(String?IfUUID)?{??41. ??42. ????????lock.lock();??43. ????????try?{??44. ????????????System.out.println("添加待調用接口UUID?激活調用線程?...?...");??45. ????????????interfaceUUIDQueue.put(IfUUID);??46. ????????????condition.signal();??47. ????????}?catch?(InterruptedException?e)?{??48. ????????????e.printStackTrace();??49. ????????}??50. ????????lock.unlock();??51. ??52. ????}??53. ??54. }??
?
?
4. 線程狀態的切換
線程的狀態轉換是線程控制的基礎。線程狀態總的可分為五大狀態:分別是生、死、可運行、運行、等待/阻塞。用一個圖來描述如下:
?
?
?
新建狀態(New):當線程對象對創建后,即進入了新建狀態,如:Thread t = new MyThread();
就緒狀態(Runnable):當調用線程對象的start()方法(t.start();),線程即進入就緒狀態。處于就緒狀態的線程,只是說明此線程已經做好了準備,隨時等待CPU調度執行,并不是說執行了t.start()此線程立即就會執行;
運行狀態(Running):當CPU開始調度處于就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就緒狀態是進入到運行狀態的唯一入口,也就是說,線程要想進入運行狀態執行,首先必須處于就緒狀態中;
阻塞狀態(Blocked):處于運行狀態中的線程由于某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。根據阻塞產生的原因不同,阻塞狀態又可以分為三種:
1.等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;
2.同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀態;
3.其他阻塞 -- 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
?
線程在Running的過程中可能會遇到阻塞(Blocked)情況
調用join()和sleep()方法,sleep()時間結束或被打斷,join()中斷,IO完成都會回到Runnable狀態,等待JVM的調度。調用wait(),使該線程處于等待池(wait blocked pool),直到notify()/notifyAll(),線程被喚醒被放到鎖定池(lock blocked pool ),釋放同步鎖使線程回到可運行狀態(Runnable)對Running狀態的線程加同步鎖(Synchronized)使其進入(lock blocked pool ),同步鎖被釋放進入可運行狀態(Runnable)。此外,在runnable狀態的線程是處于被調度的線程,此時的調度順序是不一定的。Thread類中的yield方法可以讓一個running狀態的線程轉入runnable。
?
?
?
?
5. 線程的各種機制下的示例
任務場景:一個機構有1000替代幣(類似比特幣),每次放出20個比特幣,放幣的時間間隔為 次數*3秒,放完為止。三個礦工集團(A、B、C)使用計算機挖比特幣。
用一個線程模擬機構,按照時間規律放替代幣,另外三個線程模擬三個礦工公司挖替代幣。
?
公共服務數據類/**??*?數據共享服務??*???*?@author?maoping??*??*/??public?class?MyCommonData?{????10. ????//?鎖??11. ????private?ReentrantLock?lock?=?new?ReentrantLock();??12. ??13. ????//?條件??14. ????private?Condition?condition?=?lock.newCondition();??15. ??16. ????//?倉庫??17. ????private?LinkedBlockingQueue<Integer>?bitMoneyQueue?=?new?LinkedBlockingQueue<Integer>();??18. ??19. ????//?礦工挖到比特幣??20. ????public?void?getBitMoney(String?threadName)?{??21. ????????try?{??22. ????????????lock.lock();??23. ????????????condition.await();//?處于阻塞狀態??24. ????????????Integer?moneyCode?=?bitMoneyQueue.take();//?比特幣編碼??25. ????????????System.out.println(threadName?+?"?挖到了比特幣,編碼為:"?+?moneyCode);??26. ????????}?catch?(InterruptedException?e)?{??27. ????????????e.printStackTrace();??28. ????????}?finally?{??29. ????????????lock.unlock();??30. ????????}??31. ????}??32. ??33. ????public?void?createBitCoin(Integer?bitCoinCode)?{??34. ????????try?{??35. ????????????lock.lock();??36. ????????????bitMoneyQueue.put(bitCoinCode);??37. ????????????condition.signal();??38. ????????}?catch?(InterruptedException?e)?{??39. ????????????e.printStackTrace();??40. ????????}?finally?{??41. ????????????lock.unlock();??42. ????????}??43. ??44. ????}??45. ?? 46. }??
?
2.生產數據類(此處使用單線程模擬不斷生成新的比特幣)
/**??*?創建替代幣公司??*???*?@author?maoping??*??*/??public?class?CreateBitCoinThread?extends?Thread?{????????//?共享數據??10. ????private?MyCommonData?commonData;??11. ??12. ????private?int?count?=?0;??13. ??14. ????CreateBitCoinThread(MyCommonData?commonData)?{??15. ????????this.commonData?=?commonData;??16. ??17. ????}??18. ??19. ????@Override??20. ????public?void?run()?{??21. ????????System.out.println("begin?...");??22. ????????long?nextCreateBitCoinTime?=?System.currentTimeMillis();??23. ????????long2date("首次時間為?",?nextCreateBitCoinTime);??24. ????????while?(true)?{??25. ????????????if?(count?<=?500?&&?nextCreateBitCoinTime?<?System.currentTimeMillis())?{??26. ????????????????for?(int?i?=?0;?i?<?20;?i++)?{??27. ????????????????????System.out.println("");??28. ????????????????????commonData.createBitCoin(count?*?20?+?i?+?1);??29. ????????????????}??30. ????????????????nextCreateBitCoinTime?+=?((count?+?1)?*?1000)?*?10;??31. ????????????????long2date("下次時間為?",?nextCreateBitCoinTime);??32. ????????????????count++;??33. ????????????}??34. ????????}??35. ????}??36. ??37. ????private?void?long2date(String?str,?long?dateTime)?{??38. ????????SimpleDateFormat?sdf?=?new?SimpleDateFormat("MM/dd/yyyy?HH:mm:ss");??39. ????????java.util.Date?dt?=?new?Date(dateTime);??40. ????????String?sDateTime?=?sdf.format(dt);?//?得到精確到秒的表示:08/31/2006?21:08:00??41. ????????System.out.println(str?+?"?"?+?sDateTime);??42. ????}??43. ??44. }??
3.多線程消費數據類
/**??*?創建替代幣公司??*???*?@author?maoping??*??*/??public?class?CreateBitCoinThread?extends?Thread?{????????//?共享數據??10. ????private?MyCommonData?commonData;??11. ??12. ????private?int?count?=?0;??13. ??14. ????CreateBitCoinThread(MyCommonData?commonData)?{??15. ????????this.commonData?=?commonData;??16. ??17. ????}??18. ??19. ????@Override??20. ????public?void?run()?{??21. ????????System.out.println("begin?...");??22. ????????long?nextCreateBitCoinTime?=?System.currentTimeMillis();??23. ????????long2date("首次時間為?",?nextCreateBitCoinTime);??24. ????????while?(true)?{??25. ????????????if?(count?<=?500?&&?nextCreateBitCoinTime?<?System.currentTimeMillis())?{??26. ????????????????for?(int?i?=?0;?i?<?20;?i++)?{??27. ????????????????????System.out.println("");??28. ????????????????????commonData.createBitCoin(count?*?20?+?i?+?1);??29. ????????????????}??30. ????????????????nextCreateBitCoinTime?+=?((count?+?1)?*?1000)?*?10;??31. ????????????????long2date("下次時間為?",?nextCreateBitCoinTime);??32. ????????????????count++;??33. ????????????}??34. ????????}??35. ????}??36. ??37. ????private?void?long2date(String?str,?long?dateTime)?{??38. ????????SimpleDateFormat?sdf?=?new?SimpleDateFormat("MM/dd/yyyy?HH:mm:ss");??39. ????????java.util.Date?dt?=?new?Date(dateTime);??40. ????????String?sDateTime?=?sdf.format(dt);?//?得到精確到秒的表示:08/31/2006?21:08:00??41. ????????System.out.println(str?+?"?"?+?sDateTime);??42. ????}??43. ??44. }??
main方法主類?
/**??*?main?方法主類??*???*?@author?maoping??*?10. ?*/??11. public?class?MainTest?{??
12. ??13. ????public?static?void?main(String[]?args)?{??14. ??15. ????????MyCommonData?commonData?=?new?MyCommonData();??16. ????????new?Thread(new?CreateBitCoinThread(commonData)).start();??17. ????????//?礦工挖礦??18. ????????new?Thread(new?DigBitcoinThread(commonData,?"挖礦公司?A")).start();??19. ????????new?Thread(new?DigBitcoinThread(commonData,?"挖礦公司?B")).start();??20. ????????new?Thread(new?DigBitcoinThread(commonData,?"挖礦公司?C")).start();??21. ??22. ????}??23. ??24. }??
運行結果類begin?...??首次時間為??03/26/2018?09:23:37????????挖礦公司?A?挖到了比特幣,編碼為:1??挖礦公司?B?挖到了比特幣,編碼為:2????挖礦公司?C?挖到了比特幣,編碼為:3????挖礦公司?A?挖到了比特幣,編碼為:4????挖礦公司?B?挖到了比特幣,編碼為:5????挖礦公司?C?挖到了比特幣,編碼為:6????挖礦公司?A?挖到了比特幣,編碼為:7????挖礦公司?B?挖到了比特幣,編碼為:8????挖礦公司?C?挖到了比特幣,編碼為:9????挖礦公司?A?挖到了比特幣,編碼為:10????挖礦公司?B?挖到了比特幣,編碼為:11????挖礦公司?C?挖到了比特幣,編碼為:12????挖礦公司?A?挖到了比特幣,編碼為:13????挖礦公司?B?挖到了比特幣,編碼為:14????挖礦公司?C?挖到了比特幣,編碼為:15????挖礦公司?A?挖到了比特幣,編碼為:16????挖礦公司?B?挖到了比特幣,編碼為:17????挖礦公司?C?挖到了比特幣,編碼為:18????挖礦公司?A?挖到了比特幣,編碼為:19??挖礦公司?B?挖到了比特幣,編碼為:20??下次時間為??03/26/2018?09:23:47????-----------------------????????挖礦公司?C?挖到了比特幣,編碼為:21??挖礦公司?A?挖到了比特幣,編碼為:22????????挖礦公司?B?挖到了比特幣,編碼為:23??挖礦公司?C?挖到了比特幣,編碼為:24??挖礦公司?A?挖到了比特幣,編碼為:25????????挖礦公司?B?挖到了比特幣,編碼為:26??挖礦公司?C?挖到了比特幣,編碼為:27??挖礦公司?A?挖到了比特幣,編碼為:28??????????挖礦公司?B?挖到了比特幣,編碼為:29??挖礦公司?C?挖到了比特幣,編碼為:30??挖礦公司?A?挖到了比特幣,編碼為:31????挖礦公司?B?挖到了比特幣,編碼為:32????????????挖礦公司?C?挖到了比特幣,編碼為:33??挖礦公司?A?挖到了比特幣,編碼為:34??挖礦公司?B?挖到了比特幣,編碼為:35????挖礦公司?C?挖到了比特幣,編碼為:36??挖礦公司?A?挖到了比特幣,編碼為:37??下次時間為??03/26/2018?09:24:07????-----------------------------??????挖礦公司?B?挖到了比特幣,編碼為:38??????挖礦公司?C?挖到了比特幣,編碼為:39??挖礦公司?A?挖到了比特幣,編碼為:40???? 100.挖礦公司?B?挖到了比特幣,編碼為:41??
102.挖礦公司?C?挖到了比特幣,編碼為:42??
105.挖礦公司?A?挖到了比特幣,編碼為:43??
106.挖礦公司?B?挖到了比特幣,編碼為:44??
108.挖礦公司?C?挖到了比特幣,編碼為:45??
110.挖礦公司?A?挖到了比特幣,編碼為:46??
112.挖礦公司?B?挖到了比特幣,編碼為:47??
114.挖礦公司?C?挖到了比特幣,編碼為:48??
116.挖礦公司?A?挖到了比特幣,編碼為:49??
118.挖礦公司?B?挖到了比特幣,編碼為:50??
120.挖礦公司?C?挖到了比特幣,編碼為:51??
122.挖礦公司?A?挖到了比特幣,編碼為:52??
124.挖礦公司?B?挖到了比特幣,編碼為:53??
126.挖礦公司?C?挖到了比特幣,編碼為:54??
128.挖礦公司?A?挖到了比特幣,編碼為:55??
130.挖礦公司?B?挖到了比特幣,編碼為:56??
131.挖礦公司?C?挖到了比特幣,編碼為:57??
132.下次時間為??03/26/2018?09:24:37??
由結果可以看出,每次放出20個比特幣,且每次放出時間間隔逐漸變成,第一次間隔10s 第二次間隔20s,第三次間隔30s 依次重復。且每次放出的20個比特幣被三個挖礦公司挖走。3個挖礦公司為相互競爭每次放出固定數量的比特幣。比特幣只能被其中的一個公司挖走。使用多線程描述這一情景。
?
轉載于:https://www.cnblogs.com/maopneo/p/8648816.html
總結
以上是生活随笔為你收集整理的多线程 简洁版的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。