真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

java代碼的復(fù)用提現(xiàn)在 java寫時(shí)復(fù)制原理

java中的復(fù)用類是什么意思

代碼復(fù)用能夠大大簡(jiǎn)化我們的工作。面向?qū)ο蟮恼Z言中一般是通過對(duì)類的重復(fù)使用來達(dá)到代碼復(fù)用的目的的,Java也不例外。在Java中,復(fù)用類有兩種方式,合成(has-a)與繼承(is-a)。合成就是在新的類中直接創(chuàng)建舊類的對(duì)象,這里我們復(fù)用的只是代碼的功能而不是它的形式。而繼承是在原有的類的基礎(chǔ)上建立一個(gè)新類,新類具有舊類的形式,但也加入了一些新的特性。這一章介紹的主要就是合成與繼承方面的知識(shí)。

荔城網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營(yíng)維護(hù)。成都創(chuàng)新互聯(lián)公司自2013年起到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司。

一、合成所使用的語法

合成的語法很簡(jiǎn)單,只要把要復(fù)用的類的對(duì)象的引用直接放到新類里就可以了。當(dāng)然僅僅這樣還是不夠的,我們還要?jiǎng)?chuàng)建這個(gè)類的對(duì)象讓那個(gè)引用來指向它。因?yàn)镴ava不會(huì)幫我們自動(dòng)創(chuàng)建一個(gè)缺省的對(duì)象,它只會(huì)自動(dòng)替我們把字段中的引用初始化為null。為引用賦值可以在三個(gè)地方,一個(gè)就是在定義這個(gè)引用的時(shí)候,另一個(gè)是在構(gòu)造函數(shù)中,第三個(gè)地方就是在即將要使用這個(gè)對(duì)象之前。為了防止忘記在使用前為引用賦值,我們一般應(yīng)該在前兩種場(chǎng)合來創(chuàng)建對(duì)象。如果我們要?jiǎng)?chuàng)建的這個(gè)對(duì)象會(huì)花費(fèi)很大開銷,而且又可能不是每次都需要?jiǎng)?chuàng)建它的話,我們可以考慮第三種方式來創(chuàng)建這個(gè)對(duì)象。

二、繼承所使用的語法

繼承是Java中的重要部分,因?yàn)镴ava是使用單根體系的(C++不是這樣,因?yàn)樗3窒駽的兼容),所以我們定義的每一個(gè)類都是繼承自Java中的根類Object類。在定義一個(gè)繼承自已有的類的類時(shí),要使用extends關(guān)鍵字,其后跟上基類的名字,這樣表示新定義的這個(gè)類是繼承自那個(gè)基類。在Java中不允許多重繼承(C++中允許),也就是說它不允許一個(gè)類擁有多于一個(gè)的基類,這點(diǎn)劣勢(shì)可以用接口來彌補(bǔ),因?yàn)镴ava允許一個(gè)類實(shí)現(xiàn)任意多個(gè)接口。

一個(gè)子類會(huì)自動(dòng)獲得基類中的全部字段與方法(那些由訪問控制符控制的對(duì)子類而言不可見的成員也會(huì)獲得,只是不可見,用不了),這也就是對(duì)基類中代碼的復(fù)用。除了自動(dòng)獲得自基類的代碼外,子類中還可定義新的成員,也可以覆寫基類中的方法(所謂覆寫指的是方法的聲明部分一樣但實(shí)現(xiàn)不一樣),這樣可以讓相同簽名的方法擁有不一樣的形為。

因?yàn)樽宇愖詣?dòng)擁有了基類的成員,因此在子類中自然就可以調(diào)用基類的方法。如果這個(gè)方法在子類中被覆寫過,那編譯器知道你是要調(diào)用哪個(gè)方法呢?Java提供了super關(guān)鍵字在類中表示該類的基類的引用,我們可以通過這個(gè)關(guān)鍵字來明確表示我們要用到的是基類中的成員。如果不寫super的話,那編譯器將會(huì)理解為嵌套調(diào)用。

這里有個(gè)題外話。在Java程序中常常是用public類中的main()方法做為整個(gè)程序的入口。這樣的靜態(tài)main()方法并不是非得要在public類中才能出現(xiàn)的,靜態(tài)的main()方法可以做所有類的入口(但只能是main(),而不能是其它名字的什么靜態(tài)方法)。比如一個(gè)程序有多個(gè)class組成,我們要對(duì)其中的某個(gè)class進(jìn)行單元測(cè)試時(shí),我們就可以在這個(gè)class文件中加入main(),編譯后生成這個(gè)類的.class文件,在控制臺(tái)通過java來運(yùn)行它就是了。

子類繼承了一個(gè)基類后便擁有了基類中的成員,也就可以通過創(chuàng)建的子類對(duì)象來訪問基類中可見的成員。Java是怎樣做到這一點(diǎn)的呢?在我們創(chuàng)建一個(gè)子類對(duì)象的時(shí)候,這里創(chuàng)建的已經(jīng)不是一個(gè)類的對(duì)象了,它還會(huì)創(chuàng)建這個(gè)類的基類的對(duì)象,這個(gè)基類的對(duì)象創(chuàng)建后被包括在子類的對(duì)象中。也就是說創(chuàng)建的子類的對(duì)象擁有其基類全部的成員(從這就可以知道為什么可以上傳),但是子類對(duì)象只能訪問基類中它可見的成員。那么在創(chuàng)建一個(gè)這樣的對(duì)象時(shí),子類和基類對(duì)象創(chuàng)建的順序是怎么樣的呢?為了能夠正確的初始化基類,一般會(huì)調(diào)用基類的構(gòu)造函數(shù)來進(jìn)行初始化。Java中在調(diào)用子類的構(gòu)造函數(shù)時(shí)首先會(huì)自動(dòng)的調(diào)用基類的構(gòu)造函數(shù),并且這樣的過程是層層傳遞的。比如C繼承了B,而B又繼承了A,在創(chuàng)建C的對(duì)象時(shí),C的構(gòu)造函數(shù)會(huì)首先調(diào)用B的構(gòu)造函數(shù),這時(shí)B的構(gòu)造函數(shù)又會(huì)首先調(diào)用A的構(gòu)造函數(shù)。(如果基類中沒有默認(rèn)構(gòu)造函數(shù),編譯時(shí)就會(huì)報(bào)錯(cuò)。)但是這里自動(dòng)調(diào)用的都是基類的默認(rèn)構(gòu)造函數(shù)(無參的),如果我們想調(diào)用基類的某個(gè)帶參數(shù)的構(gòu)造函數(shù)又該怎么辦呢?上面提到可以用super來代替基類的引用,與在構(gòu)造函數(shù)中通過this調(diào)用本類其它構(gòu)造函數(shù)的形式一樣,我們可以通過super來調(diào)用基類帶參數(shù)的構(gòu)造函數(shù),比如“super(i, j)”。與調(diào)用本類的其它構(gòu)造函數(shù)一樣,對(duì)基類構(gòu)造函數(shù)的顯示調(diào)用也需要放在子類構(gòu)造函數(shù)的最前面,在它之前不能有任何東西,如果基類的構(gòu)造函數(shù)會(huì)拋出異常需要捕獲的話,就會(huì)比較麻煩。

簡(jiǎn)述JAVA中繼承實(shí)現(xiàn)代碼復(fù)用

看看下面這個(gè)例子,就會(huì)明白了:JAVA中繼承可以實(shí)現(xiàn)代碼復(fù)用,

由于在父類中已經(jīng)定義的方法,被子類繼承以后,就可以使用,實(shí)現(xiàn)了代碼的復(fù)用

class Father{

private int moneyDollar=300;

int moneyHK=200;

int add(int x,int y){

return x+y;

}

}

class Son extends Father{

int moneyRMB=800;

public void changMoneyHK(int x){

moneyHK=x;

}

public void changMoneyRMB(int x){

moneyRMB=x;

}

int subs(int x,int y){

return x-y;

}

}

class GrandSon extends Son{

int multi(int x,int y){

return x*y;

}

}

public class Example5_1{

public static void main(String args[]){

int a=5,b=3;

Son son=new Son();

GrandSon sunzi=new GrandSon();

son.changMoneyHK(666);

son.changMoneyRMB(5000);

System.out.println("兒子的港幣是繼承的屬性,當(dāng)前的值是:"+son.moneyHK);

System.out.println("兒子的人民幣是新增的屬性,當(dāng)前的值是:"+son.moneyRMB);

System.out.printf("減法是兒子新增的功能,%d-%d等于%d\n",a,b,son.subs(a,b));

System.out.printf("加法是兒子繼承的功能,%d+%d等于%d\n",a,b,son.add(a,b));

System.out.println("孫子的港幣和人民幣都是繼承的屬性,,當(dāng)前的值是:");

System.out.println("港幣:"+sunzi.moneyHK+" 人民幣:"+sunzi.moneyRMB);

System.out.printf("乘法是孫子新增的功能,%d*%d等于%d\n",a,b,sunzi.multi(a,b));

System.out.printf("加法是孫子繼承的功能,%d+%d等于%d\n",a,b,sunzi.add(a,b));

System.out.printf("減法是孫子繼承的功能,%d-%d等于%d\n",a,b,sunzi.subs(a,b));

}

}

java三大特征

Java三大特性

Java語言的三大特性即是:封裝、繼承、多態(tài)

封裝:

首先,屬性可用來描述同一類事物的特征,方法可描述一類事物可做的操作。封裝就是把屬于同一類事物的共性(包括屬性與方法)歸到一個(gè)類中,以方便使用。

1.概念:封裝也稱為信息隱藏,是指利用抽象數(shù)據(jù)類型將數(shù)據(jù)和基于數(shù)據(jù)的操作封裝在一起,使其構(gòu)成一個(gè)不可分割的獨(dú)立實(shí)體,數(shù)據(jù)被保護(hù)在抽象數(shù)據(jù)類型的內(nèi)部,盡可能地隱藏內(nèi)部的細(xì)節(jié),只保留一些對(duì)外接口使之與外部發(fā)生聯(lián)系。系統(tǒng)的其他部分只有通過包裹在數(shù)據(jù)外面的被授權(quán)的操作來與這個(gè)抽象數(shù)據(jù)類型交流與交互。也就是說,用戶無需知道對(duì)象內(nèi)部方法的實(shí)現(xiàn)細(xì)節(jié),但可以根據(jù)對(duì)象提供的外部接口(對(duì)象名和參數(shù))訪問該對(duì)象。

2.好處:(1)實(shí)現(xiàn)了專業(yè)的分工。將能實(shí)現(xiàn)某一特定功能的代碼封裝成一個(gè)獨(dú)立的實(shí)體后,各程序員可以在需要的時(shí)候調(diào)用,從而實(shí)現(xiàn)了專業(yè)的分工。(2)隱藏信息,實(shí)現(xiàn)細(xì)節(jié)。通過控制訪問權(quán)限可以將可以將不想讓客戶端程序員看到的信息隱藏起來,如某客戶的銀行的密碼需要保密,只能對(duì)該客戶開發(fā)權(quán)限。

繼承:

就是個(gè)性對(duì)共性的屬性與方法的接受,并加入個(gè)性特有的屬性與方法

1.概念:一個(gè)類繼承另一個(gè)類,則稱繼承的類為子類,被繼承的類為父類。

2.目的:實(shí)現(xiàn)代碼的復(fù)用。

3.理解:子類與父類的關(guān)系并不是日常生活中的父子關(guān)系,子類與父類而是一種特殊化與一般化的關(guān)系,是is-a的關(guān)系,子類是父類更加詳細(xì)的分類。如class dog extends animal,就可以理解為dog is a animal.注意設(shè)計(jì)繼承的時(shí)候,若要讓某個(gè)類能繼承,父類需適當(dāng)開放訪問權(quán)限,遵循里氏代換原則,即向修改關(guān)閉對(duì)擴(kuò)展開放,也就是開-閉原則。

4.結(jié)果:繼承后子類自動(dòng)擁有了父類的屬性和方法,但特別注意的是,父類的私有屬性和構(gòu)造方法并不能被繼承。

另外子類可以寫自己特有的屬性和方法,目的是實(shí)現(xiàn)功能的擴(kuò)展,子類也可以復(fù)寫父類的方法即方法的重寫。

多態(tài):

多態(tài)的概念發(fā)展出來,是以封裝和繼承為基礎(chǔ)的。

多態(tài)就是在抽象的層面上實(shí)施一個(gè)統(tǒng)一的行為,到個(gè)體(具體)的層面上時(shí),這個(gè)統(tǒng)一的行為會(huì)因?yàn)閭€(gè)體(具體)的形態(tài)特征而實(shí)施自己的特征行為。(針對(duì)一個(gè)抽象的事,對(duì)于內(nèi)部個(gè)體又能找到其自身的行為去執(zhí)行。)

1.概念:相同的事物,調(diào)用其相同的方法,參數(shù)也相同時(shí),但表現(xiàn)的行為卻不同。

2.理解:子類以父類的身份出現(xiàn),但做事情時(shí)還是以自己的方法實(shí)現(xiàn)。子類以父類的身份出現(xiàn)需要向上轉(zhuǎn)型(upcast),其中向上轉(zhuǎn)型是由JVM自動(dòng)實(shí)現(xiàn)的,是安全的,但向下轉(zhuǎn)型(downcast)是不安全的,需要強(qiáng)制轉(zhuǎn)換。子類以父類的身份出現(xiàn)時(shí)自己特有的屬性和方法將不能使用。

Java線程池中的核心線程是如何被重復(fù)利用的

Java線程池中的核心線程是如何被重復(fù)利用的?

引言

在Java開發(fā)中,經(jīng)常需要?jiǎng)?chuàng)建線程去執(zhí)行一些任務(wù),實(shí)現(xiàn)起來也非常方便,但如果并發(fā)的線程數(shù)量很多,并且每個(gè)線程都是執(zhí)行一個(gè)時(shí)間很短的任務(wù)就結(jié)束了,這樣頻繁創(chuàng)建線程就會(huì)大大降低系統(tǒng)的效率,因?yàn)轭l繁創(chuàng)建線程和銷毀線程需要時(shí)間。此時(shí),我們很自然會(huì)想到使用線程池來解決這個(gè)問題。

使用線程池的好處:

降低資源消耗。java中所有的池化技術(shù)都有一個(gè)好處,就是通過復(fù)用池中的對(duì)象,降低系統(tǒng)資源消耗。設(shè)想一下如果我們有n多個(gè)子任務(wù)需要執(zhí)行,如果我們?yōu)槊總€(gè)子任務(wù)都創(chuàng)建一個(gè)執(zhí)行線程,而創(chuàng)建線程的過程是需要一定的系統(tǒng)消耗的,最后肯定會(huì)拖慢整個(gè)系統(tǒng)的處理速度。而通過線程池我們可以做到復(fù)用線程,任務(wù)有多個(gè),但執(zhí)行任務(wù)的線程可以通過線程池來復(fù)用,這樣減少了創(chuàng)建線程的開銷,系統(tǒng)資源利用率得到了提升。

降低管理線程的難度。多線程環(huán)境下對(duì)線程的管理是最容易出現(xiàn)問題的,而線程池通過框架為我們降低了管理線程的難度。我們不用再去擔(dān)心何時(shí)該銷毀線程,如何最大限度的避免多線程的資源競(jìng)爭(zhēng)。這些事情線程池都幫我們代勞了。

提升任務(wù)處理速度。線程池中長(zhǎng)期駐留了一定數(shù)量的活線程,當(dāng)任務(wù)需要執(zhí)行時(shí),我們不必先去創(chuàng)建線程,線程池會(huì)自己選擇利用現(xiàn)有的活線程來處理任務(wù)。

很顯然,線程池一個(gè)很顯著的特征就是“長(zhǎng)期駐留了一定數(shù)量的活線程”,避免了頻繁創(chuàng)建線程和銷毀線程的開銷,那么它是如何做到的呢?我們知道一個(gè)線程只要執(zhí)行完了run()方法內(nèi)的代碼,這個(gè)線程的使命就完成了,等待它的就是銷毀。既然這是個(gè)“活線程”,自然是不能很快就銷毀的。為了搞清楚這個(gè)“活線程”是如何工作的,下面通過追蹤源碼來看看能不能解開這個(gè)疑問。

分析方法

在分析源碼之前先來思考一下要怎么去分析,源碼往往是比較復(fù)雜的,如果知識(shí)儲(chǔ)備不夠豐厚,很有可能會(huì)讀不下去,或者讀岔了。一般來講要時(shí)刻緊跟著自己的目標(biāo)來看代碼,跟目標(biāo)關(guān)系不大的代碼可以不理會(huì)它,一些異常的處理也可以暫不理會(huì),先看正常的流程。就我們現(xiàn)在要分析的源碼而言,目標(biāo)就是看看線程是如何被復(fù)用的。那么對(duì)于線程池的狀態(tài)的管理以及非正常狀態(tài)下的處理代碼就可以不理會(huì),具體來講,在ThreadPollExcutor類中,有一個(gè)字段?private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));?是對(duì)線程池的運(yùn)行狀態(tài)和線程池中有效線程的數(shù)量進(jìn)行控制的, 它包含兩部分信息: 線程池的運(yùn)行狀態(tài) (runState) 和線程池內(nèi)有效線程的數(shù)量 (workerCount),還有幾個(gè)對(duì)ctl進(jìn)行計(jì)算的方法:

// 獲取運(yùn)行狀態(tài)

private static int runStateOf(int c) ? ? { return c ~CAPACITY; }

// 獲取活動(dòng)線程數(shù)

private static int workerCountOf(int c) ?{ return c CAPACITY; }123456

以上兩個(gè)方法在源碼中經(jīng)常用到,結(jié)合我們的目標(biāo),對(duì)運(yùn)行狀態(tài)的一些判斷及處理可以不用去管,而對(duì)當(dāng)前活動(dòng)線程數(shù)要加以關(guān)注等等。

下面將遵循這些原則來分析源碼。

解惑

當(dāng)我們要向線程池添加一個(gè)任務(wù)時(shí)是調(diào)用ThreadPollExcutor對(duì)象的execute(Runnable command)方法來完成的,所以先來看看ThreadPollExcutor類中的execute(Runnable command)方法的源碼:

public void execute(Runnable command) {

if (command == null)

?throw new NullPointerException();

int c = ctl.get();

if (workerCountOf(c) corePoolSize) {

?if (addWorker(command, true))

? ? ?return;

?c = ctl.get();

}

if (isRunning(c) workQueue.offer(command)) {

?int recheck = ctl.get();

?if (! isRunning(recheck) remove(command))

? ? ?reject(command);

?else if (workerCountOf(recheck) == 0)

? ? ?addWorker(null, false);

}

else if (!addWorker(command, false))

?reject(command);

}123456789101112131415161718192021

按照我們?cè)诜治龇椒ㄖ刑岬降囊恍┰瓌t,去掉一些相關(guān)性不強(qiáng)的代碼,看看核心代碼是怎樣的。

// 為分析而簡(jiǎn)化后的代碼

public void execute(Runnable command) {

int c = ctl.get();

if (workerCountOf(c) corePoolSize) {

?// 如果當(dāng)前活動(dòng)線程數(shù)小于corePoolSize,則新建一個(gè)線程放入線程池中,并把任務(wù)添加到該線程中

?if (addWorker(command, true))

? ? ?return;

?c = ctl.get();

}

// 如果當(dāng)前活動(dòng)線程數(shù)大于等于corePoolSize,則嘗試將任務(wù)放入緩存隊(duì)列

if (workQueue.offer(command)) {

?int recheck = ctl.get();

?if (workerCountOf(recheck) == 0)

? ? ?addWorker(null, false);

}else {

?// 緩存已滿,新建一個(gè)線程放入線程池,并把任務(wù)添加到該線程中(此時(shí)新建的線程相當(dāng)于非核心線程)

?addWorker(command, false)

}

}12345678910111213141516171819202122

這樣一看,邏輯應(yīng)該清晰很多了。

如果 當(dāng)前活動(dòng)線程數(shù) 指定的核心線程數(shù),則創(chuàng)建并啟動(dòng)一個(gè)線程來執(zhí)行新提交的任務(wù)(此時(shí)新建的線程相當(dāng)于核心線程);

如果 當(dāng)前活動(dòng)線程數(shù) = 指定的核心線程數(shù),且緩存隊(duì)列未滿,則將任務(wù)添加到緩存隊(duì)列中;

如果 當(dāng)前活動(dòng)線程數(shù) = 指定的核心線程數(shù),且緩存隊(duì)列已滿,則創(chuàng)建并啟動(dòng)一個(gè)線程來執(zhí)行新提交的任務(wù)(此時(shí)新建的線程相當(dāng)于非核心線程);

接下來看?addWorker(Runnable firstTask, boolean core)方法

private boolean addWorker(Runnable firstTask, boolean core) {

retry:

for () {

?int c = ctl.get();

?int rs = runStateOf(c);

?// Check if queue empty only if necessary.

?if (rs = SHUTDOWN

? ? ?! (rs == SHUTDOWN

? ? ? ? firstTask == null

? ? ? ? ! workQueue.isEmpty()))

? ? ?return false;

?for () {

? ? ?int wc = workerCountOf(c);

? ? ?if (wc = CAPACITY ||

? ? ? ? ?wc = (core ? corePoolSize : maximumPoolSize))

? ? ? ? ?return false;

? ? ?if (compareAndIncrementWorkerCount(c))

? ? ? ? ?break retry;

? ? ?c = ctl.get(); ?// Re-read ctl

? ? ?if (runStateOf(c) != rs)

? ? ? ? ?continue retry;

? ? ?// else CAS failed due to workerCount change; retry inner loop

?}

}

boolean workerStarted = false;

boolean workerAdded = false;

Worker w = null;

try {

?w = new Worker(firstTask);

?final Thread t = w.thread;

?if (t != null) {

? ? ?final ReentrantLock mainLock = this.mainLock;

? ? ?mainLock.lock();

? ? ?try {

? ? ? ? ?// Recheck while holding lock.

? ? ? ? ?// Back out on ThreadFactory failure or if

? ? ? ? ?// shut down before lock acquired.

? ? ? ? ?int rs = runStateOf(ctl.get());

? ? ? ? ?if (rs SHUTDOWN ||

? ? ? ? ? ? ?(rs == SHUTDOWN firstTask == null)) {

? ? ? ? ? ? ?if (t.isAlive()) // precheck that t is startable

? ? ? ? ? ? ? ? ?throw new IllegalThreadStateException();

? ? ? ? ? ? ?workers.add(w);

? ? ? ? ? ? ?int s = workers.size();

? ? ? ? ? ? ?if (s largestPoolSize)

? ? ? ? ? ? ? ? ?largestPoolSize = s;

? ? ? ? ? ? ?workerAdded = true;

? ? ? ? ?}

? ? ?} finally {

? ? ? ? ?mainLock.unlock();

? ? ?}

? ? ?if (workerAdded) {

? ? ? ? ?t.start();

? ? ? ? ?workerStarted = true;

? ? ?}

?}

} finally {

?if (! workerStarted)

? ? ?addWorkerFailed(w);

}

return workerStarted;

}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667

同樣,我們也來簡(jiǎn)化一下:

// 為分析而簡(jiǎn)化后的代碼

private boolean addWorker(Runnable firstTask, boolean core) {

int wc = workerCountOf(c);

if (wc = (core ? corePoolSize : maximumPoolSize))

?// 如果當(dāng)前活動(dòng)線程數(shù) = 指定的核心線程數(shù),不創(chuàng)建核心線程

?// 如果當(dāng)前活動(dòng)線程數(shù) = 指定的最大線程數(shù),不創(chuàng)建非核心線程 ?

?return false;

boolean workerStarted = false;

boolean workerAdded = false;

Worker w = null;

try {

?// 新建一個(gè)Worker,將要執(zhí)行的任務(wù)作為參數(shù)傳進(jìn)去

?w = new Worker(firstTask);

?final Thread t = w.thread;

?if (t != null) {

? ? ?workers.add(w);

? ? ?workerAdded = true;

? ? ?if (workerAdded) {

? ? ? ? ?// 啟動(dòng)剛剛新建的那個(gè)worker持有的線程,等下要看看這個(gè)線程做了啥

? ? ? ? ?t.start();

? ? ? ? ?workerStarted = true;

? ? ?}

?}

} finally {

?if (! workerStarted)

? ? ?addWorkerFailed(w);

}

return workerStarted;

}1234567891011121314151617181920212223242526272829303132

看到這里,我們大概能猜測(cè)到,addWorker方法的功能就是新建一個(gè)線程并啟動(dòng)這個(gè)線程,要執(zhí)行的任務(wù)應(yīng)該就是在這個(gè)線程中執(zhí)行。為了證實(shí)我們的這種猜測(cè)需要再來看看Worker這個(gè)類。

private final class Worker

extends AbstractQueuedSynchronizer

implements Runnable{

// ....

}

Worker(Runnable firstTask) {

setState(-1); // inhibit interrupts until runWorker

this.firstTask = firstTask;

this.thread = getThreadFactory().newThread(this);

}123456789101112

從上面的Worker類的聲明可以看到,它實(shí)現(xiàn)了Runnable接口,以及從它的構(gòu)造方法中可以知道待執(zhí)行的任務(wù)賦值給了它的變量firstTask,并以它自己為參數(shù)新建了一個(gè)線程賦值給它的變量thread,那么運(yùn)行這個(gè)線程的時(shí)候其實(shí)就是執(zhí)行Worker的run()方法,來看一下這個(gè)方法:

public void run() {

?runWorker(this);

}

final void runWorker(Worker w) {

Thread wt = Thread.currentThread();

Runnable task = w.firstTask;

w.firstTask = null;

w.unlock(); // allow interrupts

boolean completedAbruptly = true;

try {

?while (task != null || (task = getTask()) != null) {

? ? ?w.lock();

? ? ?// If pool is stopping, ensure thread is interrupted;

? ? ?// if not, ensure thread is not interrupted. ?This

? ? ?// requires a recheck in second case to deal with

? ? ?// shutdownNow race while clearing interrupt

? ? ?if ((runStateAtLeast(ctl.get(), STOP) ||

? ? ? ? ? (Thread.interrupted()

? ? ? ? ? ?runStateAtLeast(ctl.get(), STOP)))

? ? ? ? ?!wt.isInterrupted())

? ? ? ? ?wt.interrupt();

? ? ?try {

? ? ? ? ?beforeExecute(wt, task);

? ? ? ? ?Throwable thrown = null;

? ? ? ? ?try {

? ? ? ? ? ? ?task.run();

? ? ? ? ?} catch (RuntimeException x) {

? ? ? ? ? ? ?thrown = x; throw x;

? ? ? ? ?} catch (Error x) {

? ? ? ? ? ? ?thrown = x; throw x;

? ? ? ? ?} catch (Throwable x) {

? ? ? ? ? ? ?thrown = x; throw new Error(x);

? ? ? ? ?} finally {

? ? ? ? ? ? ?afterExecute(task, thrown);

? ? ? ? ?}

? ? ?} finally {

? ? ? ? ?task = null;

? ? ? ? ?w點(diǎn)抗 pletedTasks++;

? ? ? ? ?w.unlock();

? ? ?}

?}

?completedAbruptly = false;

} finally {

?processWorkerExit(w, completedAbruptly);

}

}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748

在run()方法中只調(diào)了一下 runWorker(this) 方法,再來簡(jiǎn)化一下這個(gè) runWorker() 方法

// 為分析而簡(jiǎn)化后的代碼

final void runWorker(Worker w) {

Runnable task = w.firstTask;

w.firstTask = null;

while (task != null || (task = getTask()) != null) {

? ? ?try {

? ? ? ? ?task.run();

? ? ?} finally {

? ? ? ? ?task = null;

? ? ?}

?}

}12345678910111213

很明顯,runWorker()方法里面執(zhí)行了我們新建Worker對(duì)象時(shí)傳進(jìn)去的待執(zhí)行的任務(wù),到這里為止貌似這個(gè)worker的run()方法就執(zhí)行完了,既然執(zhí)行完了那么這個(gè)線程也就沒用了,只有等待虛擬機(jī)銷毀了。那么回顧一下我們的目標(biāo):Java線程池中的核心線程是如何被重復(fù)利用的?好像并沒有重復(fù)利用啊,新建一個(gè)線程,執(zhí)行一個(gè)任務(wù),然后就結(jié)束了,銷毀了。沒什么特別的啊,難道有什么地方漏掉了,被忽略了?再仔細(xì)看一下runWorker()方法的代碼,有一個(gè)while循環(huán),當(dāng)執(zhí)行完firstTask后task==null了,那么就會(huì)執(zhí)行判斷條件?(task = getTask()) != null,我們假設(shè)這個(gè)條件成立的話,那么這個(gè)線程就不止只執(zhí)行一個(gè)任務(wù)了,可以執(zhí)行多個(gè)任務(wù)了,也就實(shí)現(xiàn)了重復(fù)利用了。答案呼之欲出了,接著看getTask()方法

private Runnable getTask() {

boolean timedOut = false; // Did the last poll() time out?

for () {

?int c = ctl.get();

?int rs = runStateOf(c);

?// Check if queue empty only if necessary.

?if (rs = SHUTDOWN (rs = STOP || workQueue.isEmpty())) {

? ? ?decrementWorkerCount();

? ? ?return null;

?}

?int wc = workerCountOf(c);

?// Are workers subject to culling?

?boolean timed = allowCoreThreadTimeOut || wc corePoolSize;

?if ((wc maximumPoolSize || (timed timedOut))

? ? ? (wc 1 || workQueue.isEmpty())) {

? ? ?if (compareAndDecrementWorkerCount(c))

? ? ? ? ?return null;

? ? ?continue;

?}

?try {

? ? ?Runnable r = timed ?

? ? ? ? ?workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :

? ? ? ? ?workQueue.take();

? ? ?if (r != null)

? ? ? ? ?return r;

? ? ?timedOut = true;

?} catch (InterruptedException retry) {

? ? ?timedOut = false;

?}

}

}1234567891011121314151617181920212223242526272829303132333435363738

老規(guī)矩,簡(jiǎn)化一下代碼來看:

// 為分析而簡(jiǎn)化后的代碼

private Runnable getTask() {

boolean timedOut = false;

for () {

?int c = ctl.get();

?int wc = workerCountOf(c);

?// timed變量用于判斷是否需要進(jìn)行超時(shí)控制。

?// allowCoreThreadTimeOut默認(rèn)是false,也就是核心線程不允許進(jìn)行超時(shí);

?// wc corePoolSize,表示當(dāng)前線程池中的線程數(shù)量大于核心線程數(shù)量;

?// 對(duì)于超過核心線程數(shù)量的這些線程,需要進(jìn)行超時(shí)控制

?boolean timed = allowCoreThreadTimeOut || wc corePoolSize;

?if (timed timedOut) {

? ? ?// 如果需要進(jìn)行超時(shí)控制,且上次從緩存隊(duì)列中獲取任務(wù)時(shí)發(fā)生了超時(shí),那么嘗試將workerCount減1,即當(dāng)前活動(dòng)線程數(shù)減1,

? ? ?// 如果減1成功,則返回null,這就意味著runWorker()方法中的while循環(huán)會(huì)被退出,其對(duì)應(yīng)的線程就要銷毀了,也就是線程池中少了一個(gè)線程了

? ? ?if (compareAndDecrementWorkerCount(c))

? ? ? ? ?return null;

? ? ?continue;

?}

?try {

? ? ?Runnable r = timed ?

? ? ? ? ?workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :

? ? ? ? ?workQueue.take();

? ? ?// 注意workQueue中的poll()方法與take()方法的區(qū)別

? ? ?//poll方式取任務(wù)的特點(diǎn)是從緩存隊(duì)列中取任務(wù),最長(zhǎng)等待keepAliveTime的時(shí)長(zhǎng),取不到返回null

? ? ?//take方式取任務(wù)的特點(diǎn)是從緩存隊(duì)列中取任務(wù),若隊(duì)列為空,則進(jìn)入阻塞狀態(tài),直到能取出對(duì)象為止

? ? ?if (r != null)

? ? ? ? ?return r;

? ? ?timedOut = true;

?} catch (InterruptedException retry) {

? ? ?timedOut = false;

?}

}

}123456789101112131415161718192021222324252627282930313233343536373839

從以上代碼可以看出,getTask()的作用是

如果當(dāng)前活動(dòng)線程數(shù)大于核心線程數(shù),當(dāng)去緩存隊(duì)列中取任務(wù)的時(shí)候,如果緩存隊(duì)列中沒任務(wù)了,則等待keepAliveTime的時(shí)長(zhǎng),此時(shí)還沒任務(wù)就返回null,這就意味著runWorker()方法中的while循環(huán)會(huì)被退出,其對(duì)應(yīng)的線程就要銷毀了,也就是線程池中少了一個(gè)線程了。因此只要線程池中的線程數(shù)大于核心線程數(shù)就會(huì)這樣一個(gè)一個(gè)地銷毀這些多余的線程。

如果當(dāng)前活動(dòng)線程數(shù)小于等于核心線程數(shù),同樣也是去緩存隊(duì)列中取任務(wù),但當(dāng)緩存隊(duì)列中沒任務(wù)了,就會(huì)進(jìn)入阻塞狀態(tài),直到能取出任務(wù)為止,因此這個(gè)線程是處于阻塞狀態(tài)的,并不會(huì)因?yàn)榫彺骊?duì)列中沒有任務(wù)了而被銷毀。這樣就保證了線程池有N個(gè)線程是活的,可以隨時(shí)處理任務(wù),從而達(dá)到重復(fù)利用的目的。

小結(jié)

通過以上的分析,應(yīng)該算是比較清楚地解答了“線程池中的核心線程是如何被重復(fù)利用的”這個(gè)問題,同時(shí)也對(duì)線程池的實(shí)現(xiàn)機(jī)制有了更進(jìn)一步的理解:

當(dāng)有新任務(wù)來的時(shí)候,先看看當(dāng)前的線程數(shù)有沒有超過核心線程數(shù),如果沒超過就直接新建一個(gè)線程來執(zhí)行新的任務(wù),如果超過了就看看緩存隊(duì)列有沒有滿,沒滿就將新任務(wù)放進(jìn)緩存隊(duì)列中,滿了就新建一個(gè)線程來執(zhí)行新的任務(wù),如果線程池中的線程數(shù)已經(jīng)達(dá)到了指定的最大線程數(shù)了,那就根據(jù)相應(yīng)的策略拒絕任務(wù)。

當(dāng)緩存隊(duì)列中的任務(wù)都執(zhí)行完了的時(shí)候,線程池中的線程數(shù)如果大于核心線程數(shù),就銷毀多出來的線程,直到線程池中的線程數(shù)等于核心線程數(shù)。此時(shí)這些線程就不會(huì)被銷毀了,它們一直處于阻塞狀態(tài),等待新的任務(wù)到來。

注意:?

本文所說的“核心線程”、“非核心線程”是一個(gè)虛擬的概念,是為了方便描述而虛擬出來的概念,在代碼中并沒有哪個(gè)線程被標(biāo)記為“核心線程”或“非核心線程”,所有線程都是一樣的,只是當(dāng)線程池中的線程多于指定的核心線程數(shù)量時(shí),會(huì)將多出來的線程銷毀掉,池中只保留指定個(gè)數(shù)的線程。那些被銷毀的線程是隨機(jī)的,可能是第一個(gè)創(chuàng)建的線程,也可能是最后一個(gè)創(chuàng)建的線程,或其它時(shí)候創(chuàng)建的線程。一開始我以為會(huì)有一些線程被標(biāo)記為“核心線程”,而其它的則是“非核心線程”,在銷毀多余線程的時(shí)候只銷毀那些“非核心線程”,而“核心線程”不被銷毀。這種理解是錯(cuò)誤的。

另外還有一個(gè)重要的接口 BlockingQueue 值得去了解,它定義了一些入隊(duì)出隊(duì)同步操作的方法,還可以阻塞,作用很大。


網(wǎng)站題目:java代碼的復(fù)用提現(xiàn)在 java寫時(shí)復(fù)制原理
URL標(biāo)題:http://weahome.cn/article/ddjhcci.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部