版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
多線程程序設(shè)計(jì)1內(nèi)容11.1進(jìn)程和線程 11.2創(chuàng)建線程 11.3線程狀態(tài) 11.4線程池 11.5線程安全的程序設(shè)計(jì) 11.5.1與時(shí)間有關(guān)的錯誤 11.5.2volatile保留字 11.5.3synchronized保留字 11.5.4計(jì)數(shù)器Adder 11.6獲取子線程的返回結(jié)果 11.7BlockingQueue 2進(jìn)程和線程程序在計(jì)算機(jī)上的運(yùn)行實(shí)例被稱為進(jìn)程(process)進(jìn)程是分配了資源(CPU,MEM)的程序3同一個程序,兩個進(jìn)程4進(jìn)程雖然計(jì)算器的程序只有一份,但可以運(yùn)行多個實(shí)例,即多個進(jìn)程。當(dāng)關(guān)閉計(jì)算機(jī),程序仍然在磁盤上,下次還可以啟動;但進(jìn)程全部消失。為了區(qū)分進(jìn)程,操作系統(tǒng)為每個進(jìn)程都指派了一個ID,稱為進(jìn)程號。5線程每個進(jìn)程都有自己的內(nèi)存空間和運(yùn)行狀態(tài),稱為進(jìn)程的上下文(context)。進(jìn)程中還可以創(chuàng)建多個線程(thread)來協(xié)作完成一個任務(wù)。線程就是進(jìn)程中可被調(diào)度運(yùn)行的指令序列。進(jìn)程中的線程共享進(jìn)程的內(nèi)存空間。進(jìn)程間的切換由操作系統(tǒng)管理,需要花費(fèi)一定的時(shí)間和空間代價(jià)完成;而線程間的切換代價(jià)要小得多。6多線程如何在進(jìn)程中啟動和管理多個線程以高效地完成一定的任務(wù),就是多線程程序設(shè)計(jì)(multithreadedprogramming)。多線程是Java平臺本質(zhì)的特征之一。7線程每個Java應(yīng)用進(jìn)程中至少有一個線程,稱為main線程。線程中可以創(chuàng)建新的線程。創(chuàng)建者被稱為父線程被創(chuàng)建的線程稱為子線程例如,main線程啟動后,可以創(chuàng)建并啟動線程A。這樣,線程A就與main線程一起處于可運(yùn)行狀態(tài)。“可運(yùn)行”狀態(tài)指只要允許就可以在中央處理器上執(zhí)行的狀態(tài)。好比是同學(xué)們都在舉手,只要老師允許,就可以發(fā)言一樣。8注意在多線程情形中,無法預(yù)知某個時(shí)刻哪個線程在中央處理器上執(zhí)行9Fibonacci序列根據(jù)Fibonacci序列的數(shù)學(xué)定義,計(jì)算第50個數(shù)需要知道第49個數(shù)和第48個數(shù),然后相加。要想知道第49個數(shù),就需要知道第47個數(shù)和第48個數(shù)。數(shù)越大,這個過程越耗時(shí)。計(jì)算耗時(shí)的任務(wù)也稱為“計(jì)算敏感”的任務(wù)。10FibonacciNumber.java單線程版本要等很長時(shí)間,才能依次輸出兩個任務(wù)的結(jié)果任務(wù)1:12586269025任務(wù)2:5011分析Java以啟動“main”線程方式執(zhí)行main方法。單線程中完成兩個計(jì)算任務(wù)12兩個線程分別執(zhí)行兩個任務(wù)main線程開始執(zhí)行...main線程終止子線程執(zhí)行任務(wù)2:50子線程執(zhí)行任務(wù)1:10233415513FibonacciTask.java11.2創(chuàng)建線程繼承java.lang.Thread類并覆蓋run方法實(shí)現(xiàn)java.lang.Runnable接口并覆蓋run方法實(shí)現(xiàn)java.util.concurrent.Callable接口14Thread類Thread類實(shí)現(xiàn)了Runnable接口。此外,Thread類中還設(shè)計(jì)了讓線程啟動(start),休眠(sleep)的實(shí)例方法。線程完成的功能安排在run方法中。15Fibonacci線程Fibonacci線程通過繼承Thread類獲取了線程所共有的屬性和方法把計(jì)算Fibonacci的功能安排在線程的run方法中從而定義了Fibonacci線程16Fibonacci.java線程的創(chuàng)建和運(yùn)行通過Fibonacci線程類的無參構(gòu)造方法創(chuàng)建了線程并使用引用變量t引用這個線程對象。線程被創(chuàng)建后并不會自動運(yùn)行。接下來程序讓線程t執(zhí)行start方法進(jìn)入可運(yùn)行狀態(tài)。17TestFibonacci.java匿名內(nèi)部類方式創(chuàng)建線程如果新建的線程類僅使用一次,則不必命名,直接使用匿名類創(chuàng)建線程對象。18TestFibonacciAnonymous.java構(gòu)造方法Thread類的構(gòu)造方法中至多有3個參數(shù):線程組(ThreadGroup)Runnable對象線程的名字(String)常見的構(gòu)造方法有:Thread()Thread(Runnabletask)Thread(Stringname)Thread(Runnabletask,Stringname)19命名內(nèi)部類方式創(chuàng)建線程20...//也可以使用命名內(nèi)部類ThreadAnewThreadA().start();}
//定義命名內(nèi)部類
classThreadAextendsThread{publicvoidrun(){…}}繼承Thread類創(chuàng)建線程步驟(1)編寫線程類,繼承Thread類(2)必須重寫publicvoidrun()(3)創(chuàng)建自定義線程類的對象(4)調(diào)用start()方法啟動線程21注意自JDK1.4后,Java建議不要再使用Thread類中的stop,suspend,resume方法。22注意想要啟動多線程,必須調(diào)用start方法。run()方法由JVM調(diào),什么時(shí)候調(diào)用,執(zhí)行的過程控制都有操作系統(tǒng)的CPU調(diào)度決定一個線程對象只能調(diào)用一次start()方法啟動,如果重復(fù)調(diào)用了,則將拋出以上的異?!發(fā)llgalThreadStateException”23實(shí)現(xiàn)Runnable接口創(chuàng)建線程通過實(shí)現(xiàn)Runnable接口創(chuàng)建線程的一般步驟如下:定義Runnable接口的實(shí)現(xiàn)類,實(shí)現(xiàn)抽象方法run。創(chuàng)建實(shí)現(xiàn)類的實(shí)例,這個實(shí)例稱為Runnable對象。以Runnable對象為構(gòu)造方法參數(shù),創(chuàng)建Thread對象。讓Thread對象執(zhí)行start進(jìn)入可運(yùn)行狀態(tài)。24
TestFibonacciRunnable.java使用匿名類創(chuàng)建Runnable對象25TestAnonymousRunnable.java實(shí)現(xiàn)Runnable接口創(chuàng)建線程步驟
26(1)編寫線程類,實(shí)現(xiàn)Runnable接口(2)必須重寫publicvoidrun(){}(3)創(chuàng)建自定義線程類的對象(4)通過Thread類的代理對象,調(diào)用start()方法啟動線程使用Lambda表達(dá)式創(chuàng)建線程27FibonacciRunnableLambda.java11.3線程狀態(tài)線程從被創(chuàng)建開始到終止總是處于這六個狀態(tài)之一:新建(NEW)可運(yùn)行(RUNNABLE)阻塞(BLOCKED)等待(WAITING)等時(shí)(TIMED_WAITING)終止(TERMINATED)28線程狀態(tài)29狀態(tài)轉(zhuǎn)移30線程的狀態(tài)轉(zhuǎn)移處于RUNNABLE狀態(tài)的線程如果所需的資源被鎖住,則就會轉(zhuǎn)到阻塞(BLOCKED)狀態(tài),直到另一個線程把該資源解鎖。處于RUNNABLE狀態(tài)的線程如果需要等待另一個線程完成其工作,則就會轉(zhuǎn)到阻塞(WAITING)狀態(tài),直到所等的線程終止。處于RUNNABLE狀態(tài)的線程如果需要等待另一個線程完成其工作,則就會轉(zhuǎn)到等時(shí)(TIMED_WAITING)狀態(tài),直到所等的時(shí)間到或者被中斷等待。31線程的狀態(tài)轉(zhuǎn)移
線程有三種方式進(jìn)入終止(TERMINATED)狀態(tài):run方法執(zhí)行結(jié)束線程執(zhí)行了System.exit()方法線程拋出異常(Exception)或錯誤(Error)。32“活著的”線程RUNNABLE狀態(tài)的線程隨時(shí)準(zhǔn)備在中央處理器上運(yùn)行。處于線程RUNNABLE狀態(tài)、BLOCKED狀態(tài)、WAITING狀態(tài)和TIME_WAITING狀態(tài)之一的線程都是“活著的”線程。線程的實(shí)例方法isAlive就返回線程對象是否活著。33TestAliveThread.java中斷信號處于Time_Waiting狀態(tài)的線程可以被中斷34
TestSleepThread.java等待另外一個線程終止如果需要等待另一個線程運(yùn)行終止,則調(diào)用該線程的實(shí)例方法join()使自己進(jìn)入WAITING狀態(tài)來實(shí)現(xiàn)35TestJoinThread.java
線程調(diào)度和優(yōu)先級從處于可運(yùn)行(RUNNABLE)狀態(tài)的線程中選擇一個讓其在中央處理器上執(zhí)行的過程稱為線程調(diào)度(scheduling)。36調(diào)度策略Java使用基于優(yōu)先級的搶先調(diào)度策略(priority-based,preemptive)。Java中的每個線程都有優(yōu)先級屬性。優(yōu)先級是從1到10的整數(shù)之一。使用實(shí)例方法setPriority可以改變優(yōu)先級。37優(yōu)先級Thread類中定義了三個優(yōu)先級常量:MIN_PRIORITY=1NORM_PRIORITY=5MAX_PRIORITY=10線程的實(shí)例方法getPriority返回線程的當(dāng)前優(yōu)先級。默認(rèn)優(yōu)先級是5。38搶先優(yōu)先級調(diào)度策略任何時(shí)刻,Java的運(yùn)行時(shí)刻系統(tǒng)總是從處于RUNNABLE狀態(tài)的線程中選擇優(yōu)先級最高的線程運(yùn)行。這就是基于優(yōu)先級的線程調(diào)度思想。當(dāng)高優(yōu)先級線程運(yùn)行期間又有更高優(yōu)先級的稱為RUNNABLE狀態(tài),Java運(yùn)行時(shí)刻系統(tǒng)就會中斷當(dāng)前線程的執(zhí)行,立即運(yùn)行這個新的更高優(yōu)先級的線程。這就是“搶先”的思想。39例40設(shè)置計(jì)算第40個Fabonacii數(shù)的線程的優(yōu)先級最低;設(shè)置計(jì)算絕對值的線程的優(yōu)先級最高。這樣即便計(jì)算絕對值的線程后啟動,也是先運(yùn)行。
TestThreadPriority.java11.4線程池419.5線程間的同步當(dāng)多個線程訪問同一個對象,如果沒有任何機(jī)制加以協(xié)調(diào)和控制,那么就會產(chǎn)生與時(shí)間有關(guān)的錯誤42與時(shí)間有關(guān)的錯誤43classCounter{privateintcount=1;publicvoidincrement(){count=count+1;}publicvoiddecrement(){count=count-1;}publicintgetValue(){returncount;}
}與時(shí)間有關(guān)的錯誤由于線程何時(shí)被調(diào)度運(yùn)行、能夠運(yùn)行多長時(shí)間都是不確定的,所以當(dāng)多線程訪問Counter對象時(shí),就可能產(chǎn)生與時(shí)間有關(guān)的錯誤。44假設(shè)線程A讓Counter對象執(zhí)行increment方法;線程B讓Counter對象執(zhí)行也執(zhí)行increment方法。并假設(shè)私有成員變量count的當(dāng)前值是1。那么線程A和線程B的一種可能的交替執(zhí)行序列是:45一個可能的執(zhí)行序列ThreadA:讀取變量count的當(dāng)前值到寄存器ThreadB:讀取變量count的當(dāng)前值到寄存器ThreadA:在寄存器中把當(dāng)前值增1,結(jié)果是2ThreadB:在寄存器中把當(dāng)前值減1,結(jié)果是2ThreadA:把計(jì)算結(jié)果2存入變量countThreadB:把計(jì)算結(jié)果2存入變量count46交替執(zhí)行的結(jié)果Counter對象的私有成員變量count的值經(jīng)過線程A增1和線程B增1變成了2;而不是所期望的3。也有可能沒有問題,count的最后的值是期望的3。47解決方案如果在一個線程讓Counter的實(shí)例方法increment執(zhí)行期間禁止其它線程執(zhí)行這個方法就能避免錯誤。synchronized關(guān)鍵字就來做這件事情48synchronized關(guān)鍵字49publicclassCounter{privateintcount=1;//Thevalueofthecounter.
synchronizedpublicvoidincrement(){count=count+1;}
synchronizedpublicintgetValue(){returncount;}}
synchronized關(guān)鍵字如果先線程A執(zhí)行increment方法期間線程B也要執(zhí)行increment方法,那么線程B就會轉(zhuǎn)入“BLOCKED”狀態(tài),直到線程A結(jié)束increment方法的執(zhí)行。如果一個對象有多個synchronized方法,某一時(shí)刻某個線程已經(jīng)進(jìn)入到了某個synchronized方法,那么在該方法沒有執(zhí)行完畢前,其他線程是無法訪問該對象的任何synchronized方法。50注意訪問同步的實(shí)例方法是給整個對象上鎖。一個對象上同步方法的執(zhí)行不影響另外一個對象上的同步方法的執(zhí)行。51代碼9-1052classCounter{ privateintcount=0;
publicsynchronizedintincrement(){ return++count; }
publicsynchronizedintdecrement(){ return--count; }
publicsynchronizedintgetValue(){ returncount; }}代碼9-1053publicclassCounterTaskimplementsRunnable{
@Override publicvoidrun(){ Test.counter.increment(); System.out.println(Thread.currentThread().getName()+"incrementsto"+Test.counter.getValue()); }}
代碼9-1054publicclassTest{ staticCountercounter=newCounter();
publicstaticvoidmain(String[]args){ CounterTaskt=newCounterTask(); Threadta=newThread(t,"A"); Threadtb=newThread(t,"B"); ta.start(); tb.start();
}
}代碼9-10程序的運(yùn)行結(jié)果:Aincrementsto1Bincrementsto255例使用兩個線程一起來數(shù)從5000001到10000000之間的素?cái)?shù)有多少個。這兩個線程每個線程數(shù)一半。每個線程數(shù)完后把結(jié)果累加到Counter對象上。最后程序報(bào)告Counter對象記載的總的素?cái)?shù)個數(shù)。56代碼9-1157classCounter{ privateinttotal=0;
synchronizedvoidaddUp(intx){ total=total+x; }
intgetTotal(){ returntotal; }}代碼9-1158publicclassPrimeCoutingTest{
privatestaticfinalintstart=5000000; privatestaticfinalintincrement=start/2; Counterc=newCounter();
代碼9-1159 privateclassCountPrimesThreadextendsThread{ intcount=0; intmin,max;
publicCountPrimesThread(Stringname,intmin,intmax){ this.min=min; this.max=max; this.setName(name); }
publicvoidrun(){ count=countPrimes(min,max); c.addUp(count); System.out.println(this.getName()+"統(tǒng)計(jì)的素?cái)?shù)個數(shù):"+count); System.out.println("當(dāng)前總數(shù):"+c.getTotal());
}代碼9-1160 privateintcountPrimes(intmin,intmax){ intcount=0; for(inti=min;i<=max;i++) if(isPrime(i)) count++; returncount; }
privatebooleanisPrime(intx){ inttop=(int)Math.sqrt(x); for(inti=2;i<=top;i++) if(x%i==0) returnfalse; returntrue; } }代碼9-1161 publicstaticvoidmain(String[]args){
CountPrimesThreadworkerA,workerB; PrimeCoutingTesttest=newPrimeCoutingTest(); workerA=test.newCountPrimesThread("workerA", start+1,start+increment); workerB=test.newCountPrimesThread("workerB", start+increment+1,start+2*increment);
workerA.start(); workerB.start(); }}注意這個例子實(shí)際上是使用同步方法訪問Counter對象的實(shí)例變量total。那么訪問實(shí)例total的方法還有g(shù)etTotal,可能還會設(shè)計(jì)其它方法。62注意如果只聲明addUp為同步方法而不聲明其它與實(shí)例變量total有關(guān)的方法為同步方法,那么非同步方法的執(zhí)行也會有可能破壞total的狀態(tài),從而出現(xiàn)與時(shí)間有關(guān)的錯誤。因此,與共享資源有關(guān)的所有方法都應(yīng)聲明為同步方法。63注意當(dāng)一個synchronized關(guān)鍵字修飾的方法同時(shí)又被static關(guān)鍵字修飾,這就是同步靜態(tài)方法的情形。前文介紹過,同步實(shí)例方法會將對象上鎖。但是靜態(tài)方法屬于類,不屬于對象,所以同步靜態(tài)方法的執(zhí)行會給“類”上鎖。64注意當(dāng)線程A訪問某類的靜態(tài)同步方法時(shí),如果線程B也試圖訪問該類任一靜態(tài)同步方法,那么線程B就被阻塞,直到線程A把該靜態(tài)同步方法執(zhí)行完畢。65總結(jié)線程同步方法是通過鎖來實(shí)現(xiàn),每個對象都有且僅有一個鎖。當(dāng)一個線程為對象上鎖,其它試圖為對象上鎖的線程將發(fā)生阻塞。對于同步靜態(tài)方法,鎖是針對這個類的,靜態(tài)和非靜態(tài)方法的鎖互不干擾。對于應(yīng)用synchronized設(shè)計(jì)多線程程序,關(guān)鍵要明確在哪個對象上同步。66總結(jié)一個線程為對象上鎖后,若干未執(zhí)行完同步方法就被強(qiáng)制轉(zhuǎn)換到WAITING狀態(tài)(比如執(zhí)行Thread.sleep()),對象上的鎖仍然保持。67同步塊可以約束多個線程不能同時(shí)執(zhí)行一個代碼塊。即在代碼塊(block)上進(jìn)行線程同步,稱為“同步塊”。68同步塊的語法synchronized(<對象>){
…}69同步塊意思是當(dāng)線程試圖執(zhí)行代碼塊時(shí),先把<對象>上鎖。這個對象可以是任意類的對象,當(dāng)然也可以是當(dāng)前對象this。70同步塊當(dāng)一個線程訪問synchronized(<對象>)同步代碼塊時(shí),它就為這個<對象>上鎖。結(jié)果,其它試圖為該<對象>上鎖線程都被暫時(shí)阻塞。71例讓兩個線程A和B執(zhí)行同一任務(wù):輸出當(dāng)前是第幾次循環(huán),演示如何同步代碼塊:要求在線程A執(zhí)行循環(huán)語句期間,線程B不能執(zhí)行。72代碼9-1273publicclassTaskimplementsRunnable{
@Override publicvoidrun(){ synchronized(Test.o){ for(inti=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+"loop" +i); } } }}代碼9-1274publicclassTest{ staticObjecto=newObject();
publicstaticvoidmain(String[]args){ Taskt=newTask(); Threadta=newThread(t,"A"); Threadtb=newThread(t,"B"); ta.start(); tb.start(); }
}
程序的輸出結(jié)果Aloop0Aloop1Aloop2Aloop3Aloop4Bloop0Bloop1Bloop2Bloop3Bloop475代碼9?1376publicclassTaskimplementsRunnable{
@Override publicvoidrun(){ synchronized(this){ for(inti=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+"loop"+i); } } }}同步代碼塊跟同步方法的異同1.同步方法是指對整個方法進(jìn)行加鎖同步,而同步塊是指對方法內(nèi)的某個代碼塊進(jìn)行加鎖同步。2.同步方法的鎖用的是其實(shí)例對象本身,而同步代碼塊的鎖可以自己指定。779.7原子變量每個對象上有且僅有一個鎖,這個鎖至多被一個線程擁有。synchronized關(guān)鍵字就是利用對象鎖來協(xié)調(diào)線程對對象上方法或者塊的執(zhí)行。78代碼9?1479publicclassCounter{privateintcount=0;
publicsynchronizedintincrement(){return++value;}publicsynchronizedintdecrement(){return--value;}publicsynchronizedintgetValue(){returnvalue;}}問題當(dāng)只有兩個線程訪問Counter對象時(shí),程序運(yùn)行結(jié)果正確,一切正常??墒?,當(dāng)有200個線程訪問Counter對象時(shí),對Counter對象的訪問就成了程序的瓶頸。Java虛擬機(jī)會使用大量的時(shí)間調(diào)度這些線程運(yùn)行,而一旦線程運(yùn)行僅僅做一個簡單的算術(shù)運(yùn)算。80問題大量線程會等待Counter對象上的鎖被釋放,而在等待期間又不能做其他事情。因此,某些應(yīng)用場景中,對象鎖會嚴(yán)重降低應(yīng)用的性能。81解決方案現(xiàn)代處理器增加了自動更新共享數(shù)據(jù)的指令。應(yīng)用這些指令,java.util.concurrent.atomic定義了在單個變量上的原子操作(atomicoperation)。例如,AtomicInteger類就提供了對一個整數(shù)對象增1的原子操作。82原子變量常用的原子變量的類型有:AtomicInteger、AtomicLong、AtomicBoolean和AtomicReference。對原子變量的操作由支持原子更新的硬件指令完成。83代碼9?15用AtomicInteger類型原子變量作為同步設(shè)施而不是用synchronized關(guān)鍵字。84代碼9?1585importjava.util.concurrent.atomic.AtomicInteger;
publicclassCounter{privateAtomicIntegercount=newAtomicInteger(0);
publicintincrement(){returncount.incrementAndGet();}
publicintdecrement(){returncount.decrementAndGet();}
publicintgetValue(){returncount.get();}
}AtomicIntegerAtomicInteger類的實(shí)例方法set和get具有原子訪問變量的能力,即在這些方法執(zhí)行期間不會被其它線程打斷。86AtomicInteger實(shí)例方法incrementAndGetdecrementAndGetgetAndIncrementgetAndDecrement對應(yīng)于Java語言中自增運(yùn)算(++)和自減運(yùn)算(--)。87AtomicInteger實(shí)例方法addAndGet把參數(shù)給定的值加到當(dāng)前值上并返回更新后的值。實(shí)例方法getAndAdd把參數(shù)給定的值加到當(dāng)前值上并返回更新前的值。88代碼9?16非阻塞棧的一個實(shí)現(xiàn)89代碼9?1690importjava.util.concurrent.atomic.AtomicReference;
publicclassNonblockingStack<E>{AtomicReference<Node<E>>head=newAtomicReference<Node<E>>();
代碼9?1691publicvoidpush(Eitem){Node<E>newHead=newNode<E>(item);Node<E>oldHead;do{oldHead=head.get();newHead.next=oldHead;}while(!pareAndSet(oldHead,newHead));}
代碼9?1692publicEpop(){Node<E>oldHead;Node<E>newHead;do{oldHead=head.get();if(oldHead==null)returnnull;newHead=oldHead.next;}while(!pareAndSet(oldHead,newHead));returnoldHead.item;}
代碼9?1693staticclassNode<E>{finalEitem;Node<E>next;
publicNode(Eitem){this.item=item;}}}總結(jié)基于底層硬件原子操作,從Java5.0開始鼓勵非阻塞算法協(xié)調(diào)線程同步。非阻塞算法通常要比傳統(tǒng)的基于鎖的同步途徑要快得多。949.8線程的管理者應(yīng)用程序可以使用靜態(tài)方法Thread.start啟動線程。但是,隨著父線程啟動的線程越來越多,而且父線程要等到子線程完成各自的任務(wù)時(shí),對子線程的管理就變得復(fù)雜起來。95解決方案線程池預(yù)先創(chuàng)建好了若干線程,如果有任務(wù)到來就直接拿來使用,用完之后不是直接將其關(guān)閉,而是將其返回到線程池中,給其他任務(wù)使用。這樣直接節(jié)省了創(chuàng)建和銷毀線程的時(shí)間,提升了系統(tǒng)的性能。96線程池97圖片來源:《Java性能調(diào)優(yōu)實(shí)戰(zhàn)》ThreadPoolExecutor
如果線程池中當(dāng)前執(zhí)行任務(wù)的線程數(shù)小于核心線程數(shù)corePoolSize,那么當(dāng)新的任務(wù)請求執(zhí)行時(shí)立即進(jìn)入線程池workQueue:任務(wù)等待隊(duì)列,當(dāng)達(dá)到核心線程數(shù)corePoolSize的時(shí)候入隊(duì)(默認(rèn)為LinkedBlockingQueue)如果隊(duì)列滿等原因無法入隊(duì),則直接創(chuàng)建線程進(jìn)入線程池,直到線程個數(shù)達(dá)到線程池容量maximumPoolSize線程池容量maximumPoolSize當(dāng)大于了線程池容量就會將準(zhǔn)備新加的任務(wù)由一個丟棄處理機(jī)制來處理keepAliveTime:默認(rèn)是0,當(dāng)線程沒有任務(wù)處理后空閑線程保持多長時(shí)間98ThreadPoolExecutor使用線程池一般過程是:創(chuàng)建線程池對象提交任務(wù)關(guān)閉線程池99向線程池提交任務(wù)調(diào)用線程池的execute方法來提交無返回值的任務(wù),execute方法的執(zhí)行過程如下:判斷線程池中運(yùn)行的線程數(shù)是否小于corePoolSize。如果是,則安排線程來處理任務(wù);否則執(zhí)行下一步。把任務(wù)添加等待隊(duì)列中。如果無法添加到隊(duì)列,進(jìn)入下一步;判斷線程池中運(yùn)行的線程數(shù)是否小于maximumPoolSize。如果是則新建線程處理當(dāng)前提交的任務(wù);否則拒絕處理。100關(guān)閉線程池shutdownNow和shutdown。無論那個方法,線程池給所有線程發(fā)送中斷信號,線程響應(yīng)中斷信號終止運(yùn)行。當(dāng)所有線程都關(guān)閉之后,線程池的isTerminaed()方法調(diào)用返回true。調(diào)用shutdown方法之后,線程池將不再接收新任務(wù),所有已提交的任務(wù)處理完畢后,執(zhí)行該任務(wù)的線程立刻終止;調(diào)用shutdownNow方法后,線程池會將等待隊(duì)列清空,所有線程執(zhí)行完任務(wù)后立即終止。大多數(shù)情況下應(yīng)使用shutdown方法來關(guān)閉線程池。101例用線程池來運(yùn)行10個任務(wù),每個任務(wù)都計(jì)算第46個Fabanacii數(shù)。102ThreadPoolExecutorDemo.java因?yàn)閏orePoolSize為3,maximumPoolSize為5,等待隊(duì)列容量為10,所以線程池使用了pool-1-thread-1、pool-1-thread-2、pool-1-thread-3三個線程先分別把Task1、Task2和Task3執(zhí)行完畢;然后從任務(wù)隊(duì)列中分別把Task4、Task5和Task6出隊(duì)并執(zhí)行;再從任務(wù)隊(duì)列中分別把Task7、Task8和Task9出隊(duì)并執(zhí)行;最后由pool-1-thread-1執(zhí)行Task10。注意第22行的期望輸出是在所有子線程線程結(jié)束輸出后main線程才輸出“mainisover.”實(shí)際的輸出反倒是“mainisover.”這是因?yàn)閙ain線程和10個子線程一樣處于可運(yùn)行(RUNNABLE)狀態(tài),而main線程安排好子線程運(yùn)行后就沒事兒了。103CountDownLatch要想讓main線程在所有10個子線程結(jié)束后再執(zhí)行輸出“mainisover.”的語句,則可以使用CountDownLatch。CountDownLatch讓線程一直等,直到其他線程執(zhí)行完自己的任務(wù)。104用法讓每個線程在任務(wù)的末尾調(diào)用其實(shí)例方法countDown,報(bào)告自己執(zhí)行完任務(wù)了;線程的等待者則調(diào)用其實(shí)例方法await等待所有線程完成任務(wù)。創(chuàng)建CountDownLatch對象時(shí)需要指定任務(wù)數(shù)。105用法當(dāng)調(diào)用CountDownLatch對象的實(shí)例方法countDown時(shí),任務(wù)數(shù)會被減1;當(dāng)調(diào)用await方法時(shí),判斷當(dāng)前任務(wù)數(shù)是否大于0。是,則線程會被阻塞,一直到任務(wù)數(shù)被countDown方法減到0時(shí),線程才會繼續(xù)執(zhí)行下一條語句106CountDownLatchDemo.java11.5線程安全的程序設(shè)計(jì)Counter類用于創(chuàng)建被多個線程訪問的共享對象。Counter類的實(shí)例方法increment把私有成員變量count增1;實(shí)例方法decrement則把私有成員變量count減1。由于線程何時(shí)被調(diào)度運(yùn)行、能夠運(yùn)行多長時(shí)間都是不確定的,所以當(dāng)多線程訪問Counter對象時(shí),就可能產(chǎn)生與時(shí)間有關(guān)的錯誤107與時(shí)間有關(guān)的錯誤假設(shè)線程A讓Counter對象執(zhí)行increment方法;線程B讓Counter對象執(zhí)行也執(zhí)行increment方法。并假設(shè)私有成員變量count的當(dāng)前值是1。那么線程A和線程B的一種可能的交替執(zhí)行序列是:ThreadA:讀取變量count的當(dāng)前值到寄存器ThreadB:讀取變量count的當(dāng)前值到寄存器ThreadA:在寄存器中把當(dāng)前值增1,結(jié)果是2ThreadB:在寄存器中把當(dāng)前值增1,結(jié)果是2ThreadA:把計(jì)算結(jié)果2存入變量countThreadB:把計(jì)算結(jié)果2存入變量count108這種交替執(zhí)行的結(jié)果是:Counter對象的私有成員變量count的值經(jīng)過線程A增1和線程B增1變成了2;而不是所期望的3。也有可能沒有問題,count的最后的值是期望的3。109例程序的設(shè)計(jì)意圖是1號門連續(xù)進(jìn)入3名游客;2號門連續(xù)離開3名游客。但某次的實(shí)際運(yùn)行結(jié)果是1號門進(jìn)入2名;2號門離開2名;1號門進(jìn)入1名;2號門離開1名。110ThreadInterference.java這種多線程程序設(shè)計(jì)中產(chǎn)生的與期望行為不一致的錯誤與語句運(yùn)行的時(shí)間相關(guān),稱為“與時(shí)間有關(guān)的錯誤”。為了控制進(jìn)入景區(qū)的游客數(shù)量,景區(qū)在各個入口統(tǒng)計(jì)進(jìn)入和離開景區(qū)的游客。假設(shè)某景區(qū)有兩個入口:1號門和2號門。進(jìn)入一名游客計(jì)數(shù)器增1;離開一名游客計(jì)數(shù)器減1。使用兩個線程t1和t2模擬兩個入口對計(jì)數(shù)器的訪問。假設(shè)3名游客從1號門進(jìn)入后由2號門離開線程的運(yùn)行環(huán)境111如果線程A和線程B共享內(nèi)存中的變量,那么線程A把寄存器中更新過的值寫回到內(nèi)存中去;線程B到主內(nèi)存中去讀取線程A之前已更新過的共享變量。可見性如果線程A修改了內(nèi)存中的共享變量之后,不被中斷地將其寫回內(nèi)存;線程B每次讀共享變量時(shí),都去內(nèi)存中重新讀取到寄存器,就會避免與時(shí)間有關(guān)的錯誤。線程沒有看到變量的最新值,因?yàn)樗€沒有被另一個線程寫回主存,這種問題被稱為“可見性(visibility)”問題112可見性明變量為volatile,就是要求JVM把對變量的所有寫入立即寫回主存。聲明一個變量volatile要求JVM讓其他對該變量的讀線程看到某線程對該變量的寫入操作的結(jié)果。113可見性把count聲明為volatile可以保證T2能夠看到T1的寫從輸出結(jié)果看到,讀count的線程先運(yùn)行,讀出的是0;寫count的線程接著運(yùn)行,寫入1;后面讀線程又讀了2次,分別輸出1。不會出現(xiàn)寫入1而讀出0的情況。114實(shí)現(xiàn)線程安全程序設(shè)計(jì)的需求可見性(Visibility)。確保當(dāng)一個線程修改了共享資源后,其他線程能夠立即看到這些修改。通常使用volatile保留字實(shí)現(xiàn)。原子性(Atomicity)性。如果一個對共享資源的操作包含若干動作,那么這些動作要么全部執(zhí)行完畢;要么一個也不執(zhí)行。確保在任意時(shí)刻只有一個線程能夠原子地操作共享資源。通常使用synchronized關(guān)鍵字保留字或顯式鎖來實(shí)現(xiàn)。避免死鎖(DeadlockAvoidance)。防止出現(xiàn)線程互相等待對方釋放資源而導(dǎo)致的所有線程都無法繼續(xù)執(zhí)行的情況。115volatile用volatile修飾變量在對其寫和讀之間建立happens-before關(guān)系:在任意線程“讀”之前,某線程的“寫”已經(jīng)完成。創(chuàng)建了兩個線程:線程t1用于修改變量count;另一個線程t2用于讀變量count。成員變量count前的volatile保留字保證了t2總能夠正確讀出最新的count值。116TestVolatile.javasynchronized如果兩個線程都在修改共享變量,那么僅僅使用volatile關(guān)鍵字保留字是不夠的。比如線程A執(zhí)行increament,剛在寄存器中把當(dāng)前值增1,尚未寫回內(nèi)存而退出執(zhí)行;線程B開始執(zhí)行,當(dāng)線程B并不知道線程的的工作進(jìn)度,只顧自己從內(nèi)存中讀、在自己的寄存器中更新、寫回內(nèi)存。本來被線程A和B兩次增1,但是線程A的增1被丟失了。這種多線程更新的情況下,需要使用保留字synchronized來保證變量的讀寫是原子的。117例景區(qū)有兩個門均可出入:Gate1和Gate2。無論游客從哪個門進(jìn)入,哪個門離開,都得更新計(jì)數(shù)器。118SynchronizedThread.javasynchronized方法如果一個對象有多個synchronized方法,某一時(shí)刻某個線程已經(jīng)進(jìn)入到了某個synchronized方法,那么在該方法沒有執(zhí)行完畢前,其他線程是無法訪問該對象的任何synchronized方法。119synchronized方法際上這種線程間的同步是應(yīng)用“鎖”完成的:當(dāng)線程A訪問某個對象的synchronized方法時(shí),將該對象上鎖,試圖訪問該對象的任何一個synchronized方法線程B就要進(jìn)入“BLOCKED”狀態(tài)。這種狀態(tài)直到線程A的訪問的同步方法執(zhí)行完畢(或者是拋出了異常),才將該對象上的鎖釋放掉,線程B從“BLOCKED”狀態(tài)轉(zhuǎn)回“RUNNABLE”狀態(tài),等待調(diào)度,重新訪問該對象的synchronized方法。120與共享資源有關(guān)的所有方法都應(yīng)聲明為同步方法。121同步塊在代碼塊(block)上進(jìn)行線程同步,稱為“同步塊”。同步塊的語法形式如下:synchronized(<對象>){
…}當(dāng)線程試圖執(zhí)行代碼塊時(shí),先把<對象>上鎖。這個對象可以是任意類的對象,當(dāng)然也可以是當(dāng)前對象this。122
SynchronizedBlock.javaAdderjava.util.concurrent.atomic.LongAdder降低了高并發(fā)情況下鎖的粒度,效率比較高啟動了10個線程,每個線程對計(jì)數(shù)器counter增1共12345次,最終使得計(jì)數(shù)器的值為123450程序控制這10個線程重復(fù)執(zhí)行3次每次運(yùn)行結(jié)果都是123450123LongAdderDemo.java
LongAccumulatorDemo.java11.6獲取子線程的返回結(jié)果Runnable接口的run()方法的局限在于不能夠返回值,或者拋出checked異常。當(dāng)期望線程把計(jì)算結(jié)果返回時(shí)應(yīng)讓線程實(shí)現(xiàn)Callable接口而不是Runnable接口。124Callable接口當(dāng)把Callable對象提交給線程池后,線程池接收線程的返回值,并將其以Future類型的對象返回給調(diào)用者。125Callable接口調(diào)用者可以通過get方法從Future對象中檢索運(yùn)行結(jié)果
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026年鄂爾多斯生態(tài)環(huán)境職業(yè)學(xué)院單招綜合素質(zhì)筆試參考題庫帶答案解析
- 2026江西九江市修水縣投資集團(tuán)有限公司招聘21人考試備考題庫及答案解析
- 2026貴州銅仁市第二人民醫(yī)院收費(fèi)室見習(xí)生招募1人考試備考試題及答案解析
- 2026自然資源部海島研究中心專業(yè)技術(shù)人員招聘15人考試備考題庫及答案解析
- 2026江西農(nóng)業(yè)大學(xué)國土資源與環(huán)境學(xué)院國土學(xué)院招聘臨時(shí)工1人考試備考題庫及答案解析
- 2026河北石家莊市供熱管理集團(tuán)有限公司勞務(wù)派遣制人員招聘2人考試備考試題及答案解析
- 2026年瀘州市部分企事業(yè)單位人才引進(jìn)88人備考題庫附答案詳解
- 2026年衡水市景縣人民醫(yī)院公開招聘醫(yī)護(hù)人員備考題庫及一套參考答案詳解
- 2026年鄭州市管城回族區(qū)紫東路社區(qū)衛(wèi)生服務(wù)中心招聘康復(fù)技士備考題庫及參考答案詳解一套
- 2026年維西縣人力資源市場關(guān)于公開招聘二名森林草原專業(yè)撲火隊(duì)隊(duì)員備考題庫及參考答案詳解一套
- 機(jī)器人行業(yè)薪酬調(diào)查
- 2025年事業(yè)單位面試心理素質(zhì)測試模擬試卷及答案
- 2025-2030疫苗冷鏈物流體系建設(shè)標(biāo)準(zhǔn)與第三方服務(wù)市場機(jī)會報(bào)告
- 2025年江蘇省事業(yè)單位招聘考試教師招聘體育學(xué)科專業(yè)知識試卷(秋季篇)
- 2025年中國橡膠粉改性瀝青(AR)行業(yè)市場分析及投資價(jià)值評估前景預(yù)測報(bào)告
- 【完整版】2025年自考《馬克思基本原理概論》真題及答案
- 胸外科圍手術(shù)期護(hù)理指南
- 大數(shù)據(jù)中心建設(shè)項(xiàng)目標(biāo)準(zhǔn)與工程造價(jià)指標(biāo)分析
- 2025年中山城市建設(shè)集團(tuán)有限公司“鴻鵠”專項(xiàng)人才引進(jìn)筆試參考題庫附帶答案詳解
- 吸塑機(jī)安全教育培訓(xùn)內(nèi)容課件
- 2025年上海市普陀區(qū)曹楊二中高三英語第一學(xué)期期末達(dá)標(biāo)檢測試題
評論
0/150
提交評論