日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

JavaSE学习52:细说多线程之Thread类和Runable接口

發布時間:2025/3/21 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JavaSE学习52:细说多线程之Thread类和Runable接口 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一線程創建的兩種方式比較

? ? ? 線程創建和啟動有兩種方式,這里只是列出步驟,不再進行詳細解釋。

? ? ??(1)繼承Thread類

[java]?view plaincopy print?
  • class?MyThread?extends?Thread{??
  • ?????public?void?run(){??
  • ?????????...??
  • ?????}??
  • }??
  • ??
  • MyThread?mt=new?MyThread();//創建線程??
  • mt.start();//啟動線程??
  • ? ? ??(2)實現Runnable接口

    [java]?view plaincopy print?
  • class?MyThread?implements?Runnable{??
  • ?????public?void?run(){??
  • ????????...??
  • ?????}??
  • }??
  • ??
  • MyThread?mt=new?MyThread();??
  • Thread?td=new?Thread(mt);//創建線程??
  • td.start();//啟動線程??
  • ? ? ??(3)兩種方式的比較

    ? ? ? 1)Runnable方式可以避免Thread方式由于Java單繼承特性帶來的缺陷。

    ? ? ? 2)Runnable方式的代碼可以被多個線程(Thread實例)共享,適合于多個線程處理同一資源的情況。


    ? ? ??二模擬應用場景

    ? ? ? 模擬一個火車站買票的場景,某車次還剩下5張火車票,有三個窗口去賣這5張火車票,我們使用三個線程模擬三

    個窗口同時賣這5張火車票,我們看Thread方式和Runnable方式這兩種方式模擬出一個什么樣的結果。

    ? ? ??(1)使用Thread方式模擬買票

    ? ? ? TicketsThread.java源文件代碼:

    [java]?view plaincopy print?
  • class?MyThread?extends?Thread{??
  • ????//一共有五張火車票??
  • ????private?int?ticketsCount?=?5;??
  • ????//窗口,也就是線程的名字??
  • ????private?String?name;??
  • ??
  • ????//構造方法??
  • ????public?MyThread(String?name){??
  • ????????this.name?=?name;??
  • ????}??
  • ??
  • ????public?void?run(){??
  • ????????while(ticketsCount?>?0){??
  • ????????????//如果還有票,就賣掉一張??
  • ????????????ticketsCount--;??
  • ????????????System.out.println(name+"賣了1張票,剩余票數為:"+ticketsCount);??
  • ????????}??
  • ????}??
  • }??
  • ??
  • public?class?TicketsThread{??
  • ????public?static?void?main(String[]?args){??
  • ????????//創建三個線程,模擬三個窗口買票??
  • ????????MyThread?mt1?=?new?MyThread("窗口1");??
  • ????????MyThread?mt2?=?new?MyThread("窗口2");??
  • ????????MyThread?mt3?=?new?MyThread("窗口3");??
  • ??
  • ????????//啟動三個線程,也就是窗口開始賣票??
  • ????????mt1.start();??
  • ????????mt2.start();??
  • ????????mt3.start();??
  • ????}??
  • }??
  • ? ? ? 運行結果:


    ? ? ? 得到的結果并不是我們想要的結果。

    ? ? ? 在票的數量加static修飾關鍵字得到的結果是正確的。這里不再進行演示。

    ? ? ??(2)使用Runnable方式模擬買票

    ? ? ? TicketsRunnable.java源文件代碼:

    [java]?view plaincopy print?
  • class?MyThread1?implements?Runnable{??
  • ????//一共有五張火車票??
  • ????private?int?ticketsCount?=?5;??
  • ??????
  • ????public?void?run(){??
  • ????????while(ticketsCount?>?0){??
  • ????????????//如果還有票,就賣掉一張??
  • ????????????ticketsCount--;??
  • ????????????System.out.println(Thread.currentThread().getName()+"賣了1張票,剩余票數為:"+ticketsCount);??
  • ????????}??
  • ????}??
  • }??
  • ??
  • public?class?TicketsRunnable{??
  • ????public?static?void?main(String[]?args){??
  • ????????MyThread1?mt?=?new?MyThread1();??
  • ????????//創建三個線程,模擬三個窗口買票??
  • ????????Thread?th1?=?new?Thread(mt,"窗口1");??
  • ????????Thread?th2?=?new?Thread(mt,"窗口2");??
  • ????????Thread?th3?=?new?Thread(mt,"窗口3");??
  • ??????????
  • ????????//啟動三個線程,也就是窗口開始賣票??
  • ????????th1.start();??
  • ????????th2.start();??
  • ????????th3.start();??
  • ????}??
  • }??
  • ? ? ? 得到了預期的結果:


    ? ? ? ?(3)結果分析

    ? ? ? ?由于線程的執行是隨機的,打印的結果也是隨機的。

    ? ? ? ?Thread方式

    ? ? ? ?三個線程,創建了三個Thread對象,每個線程都有自己的Thread對象,都有自己的ticketsCount變量,它們三個

    線程并不是共享ticketsCount變量,也就是每個線程都可以賣出5張火車票,即三個窗口賣出去15張火車票。

    ? ? ? ?Runnable方式

    ? ? ? ?三個線程共用一個Runnable對象,也就是三個線程共用一個ticketsCount變量,即三個窗口一共賣了5張火車票。

    ? ? ? ?前者不是一個線程三個對象,是三個Thread對象,也是三個線程,這三個線程啟動后都會執行5次賣票,實現不

    了共享“5張票”這個資源,所以輸出就會有15張票賣出去,顯然不符合實際,用Runnable就可以解決這個問題,創建

    的三個線程可以共享"5張票"這個資源。

    ? ? ? ticketsCont變量是實例變量,它的值自然是存在堆中(每個java對象在堆中都會占據一定內存,而實例變量的值就

    是存儲在這塊內存中,類似于結構體,因此每個對象對應一個ticketsCont的值),ticketsCont跟值傳遞沒有關系啊,如

    果是Runnable方式的話,傳遞的也只是MyThread對象引用的副本,不管ticketsCont的事,但是因為ticketsCont的值

    在引用和引用副本所指向的堆內存中,所以無論是引用還是引用副本改變了堆內存中ticketsCont的值,都會產生效

    果!

    ? ? ? ?這個根據你的需要來操作,這樣說吧,如果有一個比較大的資源要你下載,那么你用Thread方式那么你就只能一

    個線程去吧這個資源下載完,如果是runable方式的話你就可以?多new幾個子線程來出來,通過共享runable對象里面

    的資源來用多個子線程來下載這個資源,這樣的話,下載資源的時候 runable方法會使下載的線程多一些幾率在cpu里

    面,也會讓你下載速度變快繼承Thread類是多個線程分別完成自己的任務,實現Runnable接口是多個線程共同完成

    一個任務。

    ? ? ? ?三線程的生命周期

    ? ? ? ?線程的生命周期轉換示意圖:


    ? ? ? ?線程的生命周期:

    ? ? ? ?1)創建:新建一個線程對象,如Thread thd=new Thread()。

    ? ? ? ?2)就緒:創建了線程對象后,調用了線程的start()方法(注意:此時線程只是進入了線程隊列,等待獲取CPU服

    務,具備了運行的條件,但并不一定已經開始運行了)。

    ? ? ? ?3)運行:處于就緒狀態的線程,一旦獲取了CPU資源,便進入到運行狀態,開始執行run()方法里面的邏輯。

    ? ? ? ?4)阻塞:一個正在執行的線程在某些情況下,由于某種原因而暫時讓出了CPU資源,暫停了自己的執行,便進入

    了阻塞狀態,如調用了sleep()方法。

    ? ? ? 5)終止:線程的run()方法執行完畢,或者線程調用了stop()方法,線程便進入終止狀態。

    ? ? ? 這里我們可以用一個經典的線問題就是生產者和消費者問題的實例:

    [java]?view plaincopy print?
  • import?java.util.*;??
  • ??
  • public?class?ProducerConsumer{??
  • ????public?static?void?main(String[]?args){??
  • ????????SyncStack?ss?=?new?SyncStack();??
  • ????????Producer?p?=?new?Producer(ss);??
  • ????????Consumer?c?=?new?Consumer(ss);??
  • ??????????
  • ????????new?Thread(p).start();??
  • ????????new?Thread(c).start();??
  • ????}??
  • }??
  • ??
  • ??
  • //生產與消費對象??
  • class?WoTou{??
  • ????int?id;???
  • ??????
  • ????//構造方法??
  • ????WoTou(int?id){??
  • ????????this.id?=?id;??
  • ????}??
  • ??????
  • ????//重寫toString()方法??
  • ????public?String?toString(){??
  • ????????return?"WoTou?:?"?+?id;??
  • ????}??
  • }??
  • ??
  • ??
  • //容器類??
  • class?SyncStack{??
  • ????int?index?=?0;??
  • ????WoTou[]?arrWT?=?new?WoTou[4];??
  • ??????
  • ????public?synchronized?void?push(WoTou?wt){??
  • ????????while(index?==?arrWT.length){??
  • ????????????try{??
  • ????????????????//這里的wait()方法指的是Object類中的方法??
  • ????????????????this.wait();??
  • ????????????}catch(InterruptedException?e){??
  • ????????????????e.printStackTrace();??
  • ????????????}??
  • ????????}??
  • ????????//這里的notifyAll()方法指的是喚醒所有線程,而notify()方法喚醒一個線程??
  • ????????this.notifyAll();?????????
  • ????????arrWT[index]?=?wt;??
  • ????????index?++;??
  • ????}??
  • ??????
  • ????public?synchronized?WoTou?pop(){??
  • ????????while(index?==?0){??
  • ????????????try{??
  • ????????????????//這里的wait()方法指的是Object類中的方法??
  • ????????????????this.wait();??
  • ????????????}catch(InterruptedException?e){??
  • ????????????????e.printStackTrace();??
  • ????????????}??
  • ????????}??
  • ????????//這里的notifyAll()方法指的是喚醒所有線程,而notify()方法喚醒一個線程??
  • ????????this.notifyAll();??
  • ????????index--;??
  • ????????return?arrWT[index];??
  • ????}??
  • }??
  • ??
  • ??
  • //生產者??
  • class?Producer?implements?Runnable{??
  • ????SyncStack?ss?=?null;??
  • ??????
  • ????Producer(SyncStack?ss){??
  • ????????this.ss?=?ss;??
  • ????}??
  • ??????
  • ????public?void?run(){??
  • ????????for(int?i=0;?i<20;?i++){??
  • ????????????WoTou?wt?=?new?WoTou(i);??
  • ????????????ss.push(wt);??
  • ????????????System.out.println("生產了:"?+?wt);??
  • ????????????try{??
  • ????????????????Thread.sleep((int)(Math.random()?*?200));??
  • ????????????}catch(InterruptedException?e){??
  • ????????????????e.printStackTrace();??
  • ????????????}?????????????
  • ????????}??
  • ????}??
  • }??
  • ??
  • ??
  • //消費者??
  • class?Consumer?implements?Runnable{??
  • ????SyncStack?ss?=?null;??
  • ??????
  • ????Consumer(SyncStack?ss){??
  • ????????this.ss?=?ss;??
  • ????}??
  • ??????
  • ????public?void?run(){??
  • ????????for(int?i=0;?i<20;?i++){??
  • ????????????WoTou?wt?=?ss.pop();??
  • ????????????System.out.println("消費了:?"?+?wt);??
  • ????????????try{??
  • ????????????????Thread.sleep((int)(Math.random()?*?1000));??
  • ????????????}catch(InterruptedException?e){??
  • ????????????????e.printStackTrace();??
  • ????????????}?????????????
  • ????????}??
  • ????}??
  • }??
  • ? ? ? 運行結果:


    ? ? ??關于一些問題的解析:

    ? ? ? 執行線程sleep()方法是依然占著cpu的,操作系統認為該當前線程正在運行,不會讓出系統資源。

    ? ? ? 執行wait()方法是讓線程到等待池等待,讓出一系列的系統資源,其他線程可以根據調度占用cpu線程的資源有不

    少,但應該包含CPU資源和鎖資源這兩類。

    ? ? ? sleep(long mills):讓出CPU資源,但是不會釋放鎖資源。

    ? ? ? wait():讓出CPU資源和鎖資源。

    ? ? ? 鎖是用來線程同步的,sleep(long mills)雖然讓出了CPU,但是不會讓出鎖,其他線程可以利用CPU時間片了,但

    如果其他線程要獲取sleep(long mills)擁有的鎖才能執行,則會因為無法獲取鎖而不能執行,繼續等待。但是那些沒有

    和sleep(long mills)競爭鎖的線程,一旦得到CPU時間片即可運行了

    ? ? ? ?四守護線程

    ? ? ? ?(1)守護線程理論知識

    ? ? ? ?Java線程分為兩類:

    ? ? ? ?1)用戶線程:運行在前臺,執行具體的任務。程序的主線程、連接網絡的子線程等都是用戶線程。

    ? ? ? ?2)守護線程:運行在后臺,為其他前臺線程服務。守護線程的特點是一旦所有用戶線程都結束運行,守護線程會

    隨JVM一起結束工作。守護線程的應用:數據庫連接池中的檢測線程和JVM虛擬機啟動后的檢測線程等等。最常見的

    守護線程:垃圾回收線程

    ? ? ? ?設置守護線程:

    ? ? ? ?可以通過調用Thread類的setDaemon(true)方法來設置當前的線程為守護線程。

    ? ? ? ?使用守護線程的注意事項:

    ? ? ? ?1)setDaemon(true)必須在start()方法之前調用,否則會拋出IllegalThreadStateException異常。

    ? ? ? ?2)在守護線程中產生的新線程也是守護線程。

    ? ? ? ?3)不是所有的任務都可以分配給守護線程來執行,比如讀寫操作或者計算邏輯。

    ? ? ? ?(2)守護線程代碼示例:

    [java]?view plaincopy print?
  • import?java.io.*;??
  • import?java.util.*;??
  • ??
  • class?DaemonThread?implements?Runnable{??
  • ????public?void?run(){??
  • ????????System.out.println("進入守護線程"+Thread.currentThread().getName());??
  • ????????try{??
  • ????????????writeToFile();??
  • ????????}catch(Exception?e){??
  • ????????????e.printStackTrace();??
  • ????????}??
  • ????????System.out.println("退出守護線程");??
  • ????}??
  • ??????
  • ????private?void?writeToFile()?throws?Exception{??
  • ????????File?filename?=?new?File("E:\\Java\\JavaSE\\Thread"+File.separator+"daemon.txt");???
  • ????????OutputStream?os?=?new?FileOutputStream(filename,true);??
  • ????????int?count?=?0;??
  • ????????while(count?<?999){??
  • ????????????os.write(("\r\nword"+count).getBytes());??
  • ????????????System.out.println("守護線程"+Thread.currentThread().getName()+"向文件中寫入了word"+?count++);??
  • ????????????Thread.sleep(1000);??
  • ????????}??
  • ????}??
  • }??
  • ??
  • public?class?DaemonThreadDemo{??
  • ????public?static?void?main(String[]?args){??
  • ????????System.out.println("進入主線程"+Thread.currentThread().getName());??
  • ??????????
  • ????????DaemonThread?daemonThread?=?new?DaemonThread();??
  • ????????Thread?thread?=new?Thread(daemonThread);??
  • ????????thread.setDaemon(true);??
  • ????????thread.start();??
  • ??????????
  • ????????Scanner?sc?=?new?Scanner(System.in);??
  • ????????sc.next();??
  • ??????????
  • ????????System.out.println("退出主線程"+Thread.currentThread().getName());??
  • ????}??
  • }??
  • ? ? ? ?運行結果:



    ? ? ? ?(3)使用jstack生成線程快照

    ? ? ? ?作用:生成JVM當前時刻線程的快照(threaddump,即當前進程中所有線程的信息)。

    ? ? ? ?目的:幫助定位程序問題出現的原因,如長時間停頓、CPU占用率高等。

    ? ? ? ? jstack命令行工具

    ? ? ? ?你安裝JDK安裝目錄下的bin文件夾下:

    ? ? ? ?位置:E:\Java\develop\jdk1.8.0_25\bin


    ? ? ? ?如何使用jstack

    ? ? ? ?在cmd中輸入:jstack


    ? ? ? ?我們去任務管理器中找到一個進程的PID:


    ? ? ? ?生成快照:


    ? ? ? ?五總結

    ? ? ? ?(1)怎樣解決死鎖的問題

    ? ? ? ?1)盡量避免不必要的synchronized關鍵字。

    ? ? ? ?2)可以用其他方法替換synchronized關鍵字,比如標志不可變量。

    ? ? ? ?3)保證synchronized代碼塊簡練。

    ? ? ? ?(2)創建線程的建議

    ? ? ? ?根據兩種創建線程方法的比較,得出的結論是使用實現Runnable接口的方法創建的多線程更好一些,另外,就是

    需要重點注意,程序中的同一個資源指的是同一個Runnable對象。建議多使用Runnable這種方式創建多線程。

    ? ? ? ?(3)補充:

    ? ? ? 1)程序中的同一資源指的是同一個Runnable對象。

    ? ? ? 2)安全的賣票程序中需要加入同步(Synchronized)。我們的代碼過程中并沒有加入,如果需要完善,就必須加入

    同步鎖。


    from:?http://blog.csdn.net/erlian1992/article/details/51707369

    總結

    以上是生活随笔為你收集整理的JavaSE学习52:细说多线程之Thread类和Runable接口的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。