摘要:如果把注釋去掉,則在所以非線程都結(jié)束時(shí),自動(dòng)終止。默認(rèn)所有從線程產(chǎn)生的線程也是線程。停止線程線程完成方法后,就進(jìn)入狀態(tài)。被標(biāo)示為的區(qū)塊會(huì)被監(jiān)控,任何線程要執(zhí)行區(qū)塊都必須先獲得指定的對(duì)象鎖定。
Tread和Runnable 定義線程
實(shí)現(xiàn)Runnable接口,重寫(xiě)run()方法
繼承Thread類,重寫(xiě)run()方法
啟動(dòng)線程Runnable tortoise=new Tortoise(); Thread thread1=new Thread(tortoise); thread1.start();
Thread thread1=new Tortoise(); thread1.start();線程生命周期 daemon線程
主線程會(huì)從main()方法開(kāi)始執(zhí)行,直到main()方法結(jié)束后停止jvm.如果主線程中啟動(dòng)了額外線程,則主線程默認(rèn)會(huì)等到被啟動(dòng)的所有額外線程都執(zhí)行完run()方法才會(huì)終止jvm.
public class Daemon implements Runnable{ @Override public void run() { while(true){ System.out.println("TheViper"); } } public static void main(String[] args){ Thread thread=new Thread(new Daemon()); //thread.setDaemon(true); thread.start(); } }
TheViper TheViper TheViper ...
控制臺(tái)會(huì)不停的打印。如果把注釋去掉,thread.setDaemon(true),則在所以非daemon線程都結(jié)束時(shí),jvm自動(dòng)終止。
默認(rèn)所有從daemon線程產(chǎn)生的線程也是daemon線程。
啟動(dòng)線程后,基本狀態(tài)可分為可執(zhí)行(runnable),阻塞(blocked),執(zhí)行中(running).
在一個(gè)時(shí)間點(diǎn)上,一個(gè)cpu只能執(zhí)行一個(gè)線程,只是cpu會(huì)不斷切換線程。可以用Thread的setPriority()方法設(shè)置線程的優(yōu)先級(jí),設(shè)定值1-10,默認(rèn)為5.優(yōu)先級(jí)越高的線程,越有機(jī)會(huì)搶占cpu.
線程進(jìn)入blocked狀態(tài)可能的情況:
調(diào)用Thread.sleep()方法
進(jìn)入synchronized前,對(duì)競(jìng)爭(zhēng)對(duì)象的鎖定
調(diào)用wait()方法阻斷
等待io完成
...
一個(gè)處于blocked狀態(tài)的線程,可由另一個(gè)線程調(diào)用interrupt()方法,讓它離開(kāi)blocked狀態(tài)。
public class Daemon implements Runnable{ @Override public void run() { System.out.println("TheViper"); try { Thread.sleep(3000);//進(jìn)入blocked狀態(tài) } catch (InterruptedException e) { System.out.println("離開(kāi)blocked狀態(tài)"); } } public static void main(String[] args){ Thread thread=new Thread(new Daemon()); thread.start(); thread.interrupt(); } }
TheViper 離開(kāi)blocked狀態(tài)安插線程
假設(shè)線程A正在運(yùn)行,這時(shí)線程B想占用cpu運(yùn)行,等它運(yùn)行完后,繼續(xù)運(yùn)行線程A.這種情況可以使用join()方法.
未使用join()
public class Daemon implements Runnable{ @Override public void run() { System.out.println("線程開(kāi)始"); try { Thread.sleep(3000); System.out.println("線程執(zhí)行"); } catch (InterruptedException e) { System.out.println("離開(kāi)blocked狀態(tài)"); } System.out.println("線程結(jié)束"); } public static void main(String[] args){ Thread thread=new Thread(new Daemon()); thread.start(); System.out.println("主線程開(kāi)始"); } }
主線程開(kāi)始 線程開(kāi)始 線程執(zhí)行 線程結(jié)束
使用join()
public class Daemon implements Runnable{ @Override public void run() { System.out.println("線程開(kāi)始"); try { Thread.sleep(3000); System.out.println("線程執(zhí)行"); } catch (InterruptedException e) { System.out.println("離開(kāi)blocked狀態(tài)"); } System.out.println("線程結(jié)束"); } public static void main(String[] args) throws InterruptedException{ Thread thread=new Thread(new Daemon()); thread.start(); thread.join(); System.out.println("主線程開(kāi)始"); } }
線程開(kāi)始 線程執(zhí)行 線程結(jié)束 主線程開(kāi)始
程序啟動(dòng)后就開(kāi)始運(yùn)行主線程(因?yàn)樾陆ň€程使用了sleep(),讓新建線程進(jìn)入blocked狀態(tài),主線程可以占用cpu),對(duì)新建的線程使用join(),將其加入到主線程后,原本應(yīng)該一來(lái)就運(yùn)行的主線程,現(xiàn)在需要等到后面新建的線程執(zhí)行完后,才能繼續(xù)執(zhí)行。
如果加入的線程處理時(shí)間太久,可以在join()時(shí)指定時(shí)間,如join(10000),表示加入的線程最多只能處理10秒。
線程完成run()方法后,就進(jìn)入dead狀態(tài)。這時(shí)不能再次調(diào)用start()方法。
Thread類上的stop()方法是過(guò)時(shí)方法。
如果要停止線程,最好自行操作,讓線程跑完應(yīng)有的流程,而不是調(diào)用stop()方法
... private boolean isContinue=true; public void stop(){ this.isContinue=false; } public void run(){ while(isContinue){ ... } } ...synchronized
public synchronized void add(Object o){...}
每個(gè)對(duì)象都有一個(gè)內(nèi)部鎖定。被標(biāo)示為synchronized的區(qū)塊會(huì)被監(jiān)控,任何線程要執(zhí)行synchronized區(qū)塊都必須先獲得指定的對(duì)象鎖定。
如果線程A已獲得對(duì)象鎖定開(kāi)始執(zhí)行sychronized區(qū)塊,線程B也想執(zhí)行synchronized區(qū)塊,線程B會(huì)因?yàn)闊o(wú)法獲得對(duì)象鎖定而進(jìn)入等待對(duì)象鎖定狀態(tài),直到線程A釋放鎖定(如執(zhí)行完synchronized區(qū)塊)。
在方法上標(biāo)示sychronized,則執(zhí)行方法必須獲得該實(shí)例的指定。
實(shí)際上等待對(duì)象鎖定時(shí),也會(huì)進(jìn)入線程的blocked狀態(tài)
synchronized不僅可以聲明在方法上,也可以描述句方式使用
public void add(Object o){ synchronized(this){ ... } }
Listlist=new ArrayList (); synchronized(list){ ... list.add("TheViper");//讓ArrayList線程安全 }
這種方式不用鎖定整個(gè)方法,只鎖定會(huì)發(fā)生競(jìng)爭(zhēng)狀況的區(qū)塊。獲得鎖定的線程執(zhí)行完這個(gè)區(qū)塊后,會(huì)立即釋放鎖定,其他線程就有機(jī)會(huì)再競(jìng)爭(zhēng)這個(gè)鎖定.
相比于將整個(gè)方法聲明為synchronized,這種方式更有效率。
可以提供不同的對(duì)象作為鎖定的來(lái)源
private Object lock1=new Object(); private Object lock2=new Object(); public void method1(){ synchronized(lock1){ data1++; ... } } public void method2(){ synchronized(lock2){ data2++; ... } }
在兩個(gè)synchronized區(qū)塊里,沒(méi)有公共的數(shù)據(jù),方法。當(dāng)有一個(gè)線程執(zhí)行method1()而另一個(gè)線程執(zhí)行method2()時(shí),并不會(huì)引起共享存取問(wèn)題,而且一個(gè)線程不會(huì)因?yàn)榱硪粋€(gè)線程獲得鎖定而阻塞.
java的synchronized提供的是可重入同步,也就是線程獲得某對(duì)象鎖定后,若執(zhí)行過(guò)程中又要執(zhí)行synchronized,而這個(gè)鎖定對(duì)象的來(lái)源又和前面的是同一個(gè),則可以直接執(zhí)行。
public class Resource { synchronized void doSome(){ } public synchronized void deal(Resource res){ res.doSome(); } }
public class TheadDemo extends Thread{ Resource res1; Resource res2; TheadDemo(Resource res1,Resource res2){ this.res1=res1; this.res2=res2; } @Override public void run(){ for(int i=0;i<10;i++) this.res1.deal(this.res2); } public static void main(String[] args){ Resource res1=new Resource(); Resource res2=new Resource(); Thread thread1=new TheadDemo(res1,res2); Thread thread2=new TheadDemo(res2,res1); thread1.start(); thread2.start(); } }
這段代碼可能出現(xiàn)死鎖,也可能不會(huì)。
如果出現(xiàn)的話,原因在于
Thread thread1=new TheadDemo(res1,res2)=>this.res1.deal(this.res2)時(shí),thread1獲得res1的鎖定
Thread thread2=new TheadDemo(res2,res1)=>this.res2.deal(this.res1),thread2獲得res2的鎖定
thread1線程res2.doSome(),準(zhǔn)備獲得res2的鎖定,但是res2的鎖定被thread2線程占用,于是thread1線程進(jìn)入blocked狀態(tài)
thread2線程res1.doSome(),準(zhǔn)備獲得res1的鎖定,但是res1的鎖定被thread1線程占用,于是thread2線程進(jìn)入blocked狀態(tài)
volatilesynchronized所標(biāo)志區(qū)塊的特點(diǎn):
互斥性:synchronized區(qū)塊在一個(gè)時(shí)間點(diǎn)上只能有一個(gè)線程執(zhí)行它
可見(jiàn)性:線程離開(kāi)synchronized區(qū)塊后,另一個(gè)線程面對(duì)的是上一個(gè)線程改變后的對(duì)象狀態(tài)
在java中對(duì)于可見(jiàn)性的要求,可以使用volatile達(dá)到變量范圍。
沒(méi)用volatile,synchronized
public class Variable { static int i=0,j=0; static void deal(){ i++; j++; } static void print(){ System.out.println("i="+i+" j="+j); } }
public class VariableTest1 extends Thread{ @Override public void run(){ while(true) Variable.print(); } }
public class VariableTest extends Thread{ @Override public void run(){ while(true) Variable.deal(); } public static void main(String[] args){ Thread t1=new VariableTest(); Thread t2=new VariableTest1(); t1.start(); t2.start(); } }
... i=638864948 j=638864993 i=638866963 j=638867006 i=638868897 j=638868941 i=638870928 j=638870974 ...
可以看到j(luò)大于i,甚至可以遠(yuǎn)大于i.原因在于為了效率,線程可以快取變量的值。
t2調(diào)用Variable.print()從共享內(nèi)存中取到變量i的值,并存儲(chǔ)在自己的內(nèi)存空間,如果此時(shí)cpu切換線程至t1,并不斷的執(zhí)行Variable.deal()多次,再切回t2,取得變量j的值,然后和i值一起輸出。
如果在deal()和print()方法上標(biāo)志synchronized,則t1每次調(diào)用deal()方法時(shí),t2都必須等到t1釋放鎖定才能調(diào)用print()方法.t2每次調(diào)用print()方法也一樣。
這種情況下,輸出的i一定等于j.
在變量上聲明volatile,表示變量是不穩(wěn)定的,易變的。這樣變量就可以在多線程情況下存取,保證了變量的可見(jiàn)性.換句話說(shuō),如果有線程修改了變量的值,另一個(gè)線程一定可以看到被修改的變量。
被標(biāo)示為volatile的變量,不允許線程快取,變量的存取一定是在共享內(nèi)存中進(jìn)行。
下面將上面例子中的i,j聲明為volatile.
volatile static int i=0,j=0;
... i=34456445 j=34456448 i=34456620 j=34456623 i=34456789 j=34456791 i=34456959 j=34456961 ...
可以看到?jīng)]有出現(xiàn)j遠(yuǎn)大于i的情況,都是i略小于j.
事實(shí)上,i和j的關(guān)系可以是大于,等于,小于三種之中的任一種。
i大于j
i已經(jīng)改變,但是j仍然是上一次操作的j.
i小于j
第一次輸出時(shí),保存i,然后j++,第二次輸出的是沒(méi)有修改過(guò)的i和修改過(guò)的j.
i等于j
輸出的都是共享內(nèi)存中修改過(guò)的值.
由此可見(jiàn),volatile保證的是單一變量可見(jiàn)性,線程對(duì)變量的存取一定是在共享內(nèi)存中進(jìn)行,不會(huì)在自己的內(nèi)存空間中快取變量。線程對(duì)共享內(nèi)存中變量的存取,另一個(gè)線程一定看得到。
等待與通知wait(),notify(),notifyAll()是Object類中的方法,可以通過(guò)這三個(gè)方法控制線程釋放對(duì)象的鎖定,或者通知線程參與鎖定的競(jìng)爭(zhēng).
線程進(jìn)入synchronized區(qū)塊前,要先獲得指定對(duì)象的鎖定。而在執(zhí)行synchronized區(qū)塊代碼時(shí),若調(diào)用鎖定對(duì)象的wait()方法,線程會(huì)釋放對(duì)象鎖定,并進(jìn)入對(duì)象等待集合,其他線程這時(shí)可以競(jìng)爭(zhēng)對(duì)象鎖定,取得鎖定的線程可以執(zhí)行synchronized區(qū)塊代碼。
處于等待集合中的線程不會(huì)參與競(jìng)爭(zhēng)cpu.wait()方法可以指定等待時(shí)間,時(shí)間到之后,線程會(huì)參與競(jìng)爭(zhēng)cpu.如果指定時(shí)間為0或沒(méi)指定,則線程會(huì)一直等待,直到被中斷(interrupt)或通知(notify)可以參與競(jìng)爭(zhēng)cpu.
鎖定的對(duì)象調(diào)用notify()方法,會(huì)對(duì)象等待集合中隨機(jī)通知一個(gè)線程參與競(jìng)爭(zhēng)cpu.
如果調(diào)用的是notifyAll()方法,則等待集合中的所有線程都會(huì)參與競(jìng)爭(zhēng)cpu.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64706.html
摘要:然而,這兩個(gè)方法都只是讀取對(duì)象狀態(tài),如果只是讀取操作,就可以允許線程并行,這樣讀取效率將會(huì)提高。分配線程執(zhí)行子任務(wù)執(zhí)行子任務(wù)獲得子任務(wù)進(jìn)行完成的結(jié)果 Lock Lock接口主要操作類是ReentrantLock,可以起到synchronized的作用,另外也提供額外的功能。用Lock重寫(xiě)上一篇中的死鎖例子 import java.util.concurrent.locks.Lock; ...
摘要:一讓廣播明星黯然失色要建立頁(yè)面,需要?jiǎng)?chuàng)建用超文本標(biāo)記語(yǔ)言,編寫(xiě)的文件,把它們放在一個(gè)服務(wù)器上二服務(wù)器能做什么服務(wù)器在互聯(lián)網(wǎng)上有一份全天候的工作。一、Web讓廣播明星黯然失色 要建立Web頁(yè)面,需要?jiǎng)?chuàng)建用超文本標(biāo)記語(yǔ)言(HyperText Markup Language,HTML)編寫(xiě)的文件,把它們放在一個(gè)Web服務(wù)器上二、Web服務(wù)器能做什么? Web服務(wù)器在互聯(lián)網(wǎng)上有一份全天候的工...
摘要:包括元素的高度上下內(nèi)邊距上下邊框值,如果元素的的值為那么該值為。該值為元素的包含元素。最后,所有這些偏移量都是只讀的,而且每次訪問(wèn)他們都需要重新計(jì)算。為了避免重復(fù)計(jì)算,可以將計(jì)算的值保存起來(lái),以提高性能。 offsetHeight 包括元素的高度、上下內(nèi)邊距、上下邊框值,如果元素的style.display的值為none,那么該值為0。offsetWidth 包括元素的寬度、左...
摘要:如果需要收集參數(shù)化類型對(duì)象,只有使用警告這節(jié)討論,向參數(shù)可變的方法傳遞一個(gè)泛型類型的實(shí)例。異常不能拋出或捕獲泛型類的實(shí)例實(shí)際上,泛型類擴(kuò)展也是不合法的。 Object:所有類的超類 java中每個(gè)類都是由它擴(kuò)展而來(lái),但是并不需要這樣寫(xiě):class Employee extends Object.如果沒(méi)有明確指出超類,Object類就被認(rèn)為是這個(gè)的超類??梢允褂肙bject類型的變量引用...
摘要:關(guān)鍵字作用調(diào)用超類方法調(diào)用超類構(gòu)造器關(guān)鍵字作用引用隱式參數(shù)如調(diào)用該類的其他構(gòu)造器在覆蓋一個(gè)方法時(shí),子類方法可見(jiàn)性不能低于超類方法阻止繼承類和方法目的確保它們不會(huì)在子類中改變語(yǔ)義。但是如果將一個(gè)類聲明為后面可以改變類變量的值了。 數(shù)據(jù)類型 整型 int 存儲(chǔ)要求:4byte 取值范圍:-2147483648 -- 2147483647(超過(guò)20億) short 存儲(chǔ)要求:2byte 取...
閱讀 2615·2023-04-25 22:09
閱讀 2846·2021-10-14 09:47
閱讀 1943·2021-10-11 11:10
閱讀 2694·2021-10-09 09:44
閱讀 3390·2021-09-22 14:57
閱讀 2504·2019-08-30 15:56
閱讀 1623·2019-08-30 15:55
閱讀 783·2019-08-30 14:13