日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

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

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

一線程創(chuàng)建的兩種方式比較

? ? ? 線程創(chuàng)建和啟動有兩種方式,這里只是列出步驟,不再進(jìn)行詳細(xì)解釋。

? ? ??(1)繼承Thread類

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

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

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

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


    ? ? ??二模擬應(yīng)用場景

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

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

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

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

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


    ? ? ? 得到的結(jié)果并不是我們想要的結(jié)果。

    ? ? ? 在票的數(shù)量加static修飾關(guān)鍵字得到的結(jié)果是正確的。這里不再進(jìn)行演示。

    ? ? ??(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張票,剩余票數(shù)為:"+ticketsCount);??
  • ????????}??
  • ????}??
  • }??
  • ??
  • public?class?TicketsRunnable{??
  • ????public?static?void?main(String[]?args){??
  • ????????MyThread1?mt?=?new?MyThread1();??
  • ????????//創(chuàng)建三個線程,模擬三個窗口買票??
  • ????????Thread?th1?=?new?Thread(mt,"窗口1");??
  • ????????Thread?th2?=?new?Thread(mt,"窗口2");??
  • ????????Thread?th3?=?new?Thread(mt,"窗口3");??
  • ??????????
  • ????????//啟動三個線程,也就是窗口開始賣票??
  • ????????th1.start();??
  • ????????th2.start();??
  • ????????th3.start();??
  • ????}??
  • }??
  • ? ? ? 得到了預(yù)期的結(jié)果:


    ? ? ? ?(3)結(jié)果分析

    ? ? ? ?由于線程的執(zhí)行是隨機(jī)的,打印的結(jié)果也是隨機(jī)的。

    ? ? ? ?Thread方式

    ? ? ? ?三個線程,創(chuàng)建了三個Thread對象,每個線程都有自己的Thread對象,都有自己的ticketsCount變量,它們?nèi)齻€

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

    ? ? ? ?Runnable方式

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

    ? ? ? ?前者不是一個線程三個對象,是三個Thread對象,也是三個線程,這三個線程啟動后都會執(zhí)行5次賣票,實(shí)現(xiàn)不

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

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

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

    是存儲在這塊內(nèi)存中,類似于結(jié)構(gòu)體,因此每個對象對應(yīng)一個ticketsCont的值),ticketsCont跟值傳遞沒有關(guān)系啊,如

    果是Runnable方式的話,傳遞的也只是MyThread對象引用的副本,不管ticketsCont的事,但是因?yàn)閠icketsCont的值

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

    果!

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

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

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

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

    一個任務(wù)。

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

    ? ? ? ?線程的生命周期轉(zhuǎn)換示意圖:


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

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

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

    務(wù),具備了運(yùn)行的條件,但并不一定已經(jīng)開始運(yùn)行了)。

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

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

    了阻塞狀態(tài),如調(diào)用了sleep()方法。

    ? ? ? 5)終止:線程的run()方法執(zhí)行完畢,或者線程調(diào)用了stop()方法,線程便進(jìn)入終止?fàn)顟B(tài)。

    ? ? ? 這里我們可以用一個經(jīng)典的線問題就是生產(chǎn)者和消費(fèi)者問題的實(shí)例:

    [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();??
  • ????}??
  • }??
  • ??
  • ??
  • //生產(chǎn)與消費(fèi)對象??
  • class?WoTou{??
  • ????int?id;???
  • ??????
  • ????//構(gòu)造方法??
  • ????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];??
  • ????}??
  • }??
  • ??
  • ??
  • //生產(chǎn)者??
  • 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("生產(chǎn)了:"?+?wt);??
  • ????????????try{??
  • ????????????????Thread.sleep((int)(Math.random()?*?200));??
  • ????????????}catch(InterruptedException?e){??
  • ????????????????e.printStackTrace();??
  • ????????????}?????????????
  • ????????}??
  • ????}??
  • }??
  • ??
  • ??
  • //消費(fèi)者??
  • 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("消費(fèi)了:?"?+?wt);??
  • ????????????try{??
  • ????????????????Thread.sleep((int)(Math.random()?*?1000));??
  • ????????????}catch(InterruptedException?e){??
  • ????????????????e.printStackTrace();??
  • ????????????}?????????????
  • ????????}??
  • ????}??
  • }??
  • ? ? ? 運(yùn)行結(jié)果:


    ? ? ??關(guān)于一些問題的解析:

    ? ? ? 執(zhí)行線程sleep()方法是依然占著cpu的,操作系統(tǒng)認(rèn)為該當(dāng)前線程正在運(yùn)行,不會讓出系統(tǒng)資源。

    ? ? ? 執(zhí)行wait()方法是讓線程到等待池等待,讓出一系列的系統(tǒng)資源,其他線程可以根據(jù)調(diào)度占用cpu線程的資源有不

    少,但應(yīng)該包含CPU資源和鎖資源這兩類。

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

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

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

    如果其他線程要獲取sleep(long mills)擁有的鎖才能執(zhí)行,則會因?yàn)闊o法獲取鎖而不能執(zhí)行,繼續(xù)等待。但是那些沒有

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

    ? ? ? ?四守護(hù)線程

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

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

    ? ? ? ?1)用戶線程:運(yùn)行在前臺,執(zhí)行具體的任務(wù)。程序的主線程、連接網(wǎng)絡(luò)的子線程等都是用戶線程。

    ? ? ? ?2)守護(hù)線程:運(yùn)行在后臺,為其他前臺線程服務(wù)。守護(hù)線程的特點(diǎn)是一旦所有用戶線程都結(jié)束運(yùn)行,守護(hù)線程會

    隨JVM一起結(jié)束工作。守護(hù)線程的應(yīng)用:數(shù)據(jù)庫連接池中的檢測線程和JVM虛擬機(jī)啟動后的檢測線程等等。最常見的

    守護(hù)線程:垃圾回收線程

    ? ? ? ?設(shè)置守護(hù)線程:

    ? ? ? ?可以通過調(diào)用Thread類的setDaemon(true)方法來設(shè)置當(dāng)前的線程為守護(hù)線程。

    ? ? ? ?使用守護(hù)線程的注意事項(xiàng):

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

    ? ? ? ?2)在守護(hù)線程中產(chǎn)生的新線程也是守護(hù)線程。

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

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

    [java]?view plaincopy print?
  • import?java.io.*;??
  • import?java.util.*;??
  • ??
  • class?DaemonThread?implements?Runnable{??
  • ????public?void?run(){??
  • ????????System.out.println("進(jìn)入守護(hù)線程"+Thread.currentThread().getName());??
  • ????????try{??
  • ????????????writeToFile();??
  • ????????}catch(Exception?e){??
  • ????????????e.printStackTrace();??
  • ????????}??
  • ????????System.out.println("退出守護(hù)線程");??
  • ????}??
  • ??????
  • ????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("守護(hù)線程"+Thread.currentThread().getName()+"向文件中寫入了word"+?count++);??
  • ????????????Thread.sleep(1000);??
  • ????????}??
  • ????}??
  • }??
  • ??
  • public?class?DaemonThreadDemo{??
  • ????public?static?void?main(String[]?args){??
  • ????????System.out.println("進(jìn)入主線程"+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());??
  • ????}??
  • }??
  • ? ? ? ?運(yùn)行結(jié)果:



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

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

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

    ? ? ? ? jstack命令行工具

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

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


    ? ? ? ?如何使用jstack

    ? ? ? ?在cmd中輸入:jstack


    ? ? ? ?我們?nèi)ト蝿?wù)管理器中找到一個進(jìn)程的PID:


    ? ? ? ?生成快照:


    ? ? ? ?五總結(jié)

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

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

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

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

    ? ? ? ?(2)創(chuàng)建線程的建議

    ? ? ? ?根據(jù)兩種創(chuàng)建線程方法的比較,得出的結(jié)論是使用實(shí)現(xiàn)Runnable接口的方法創(chuàng)建的多線程更好一些,另外,就是

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

    ? ? ? ?(3)補(bǔ)充:

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

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

    同步鎖。


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

    總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。