Java并發(fā)基礎(chǔ)總結(jié)(一)

Java并發(fā)基礎(chǔ)總結(jié)(一)

并發(fā)是一種能并行運行多個程序或并行運行一個程序中多個部分的能力。如果程序中一個耗時的任務(wù)能以異步或并行的方式運行,那么整個程序的吞吐量和可 交互性將大大改善?,F(xiàn)代的PC都有多個CPU或一個CPU中有多個核,是否能合理運用多核的能力將成為一個大規(guī)模應(yīng)用程序的關(guān)鍵。

線程基本使用

編寫線程運行時執(zhí)行的代碼有兩種方式:一種是創(chuàng)建Thread子類的一個實例并重寫run方法,第二種是創(chuàng)建類的時候?qū)崿F(xiàn)Runnable接口。當然,實現(xiàn)?Callable也算是一種方式,Callable和Future結(jié)合實現(xiàn)可以實現(xiàn)在執(zhí)行完任務(wù)后獲取返回值,而Runnable和Thread方式是無法獲取任務(wù)執(zhí)行后的結(jié)果的。

public class ThreadMain {

? ? public static void main(String[] args) {

? ? ? ? MyThread myThread = new MyThread();

? ? ? ? new Thread(myThread).start();

? ? ? ? new MyThreas2().start();

? ? }

}

// 第一種方式,實現(xiàn)Runable接口

class MyThread implements Runnable {

? ? @Override

? ? public void run() {

? ? ? ? System.out.println("MyThread run...");

? ? }

}

// 第二種方式,繼承Thread類,重寫run()方法

class MyThreas2 extends Thread {

? ? @Override

? ? public void run() {

? ? ? ? System.out.println("MyThread2 run...");

? ? }

}

一旦線程啟動后start()方法會立即返回,而不會等待run()方法執(zhí)行完畢后返回,就好像run方法是在另外一個cpu上執(zhí)行一樣。

注意:創(chuàng)建并運行一個線程所犯的常見錯誤是調(diào)用線程的run()方法而非start()方法,如下所示:

Thread newThread =new Thread(MyRunnable());

newThread.run();? //should be start();

起初你并不會感覺到有什么不妥,因為run()方法的確如你所愿的被調(diào)用了。但是,事實上,run()方法并非是由剛創(chuàng)建的新線程所執(zhí)行的,而是當前線程所執(zhí)行了。也就是被執(zhí)行上面兩行代碼的線程所執(zhí)行的。想要讓創(chuàng)建的新線程執(zhí)行run()方法,必須調(diào)用新線程的start方法。

Callable和Future結(jié)合實現(xiàn)實現(xiàn)在執(zhí)行完任務(wù)后獲取返回值

public static void main(String[] args) {

? ? ExecutorService exec = Executors.newSingleThreadExecutor();

? ? Future future = exec.submit(new CallTask());

? ? System.out.println(future.get());

}

class CallTaskimplements Callable {

? ? public String call() {

? ? ? ? return"hello";

? ? }

}

給線程設(shè)置線程名:

MyTask myTask =new MyTask();

Thread thread =new Thread(myTask, "myTask thread");

thread.start();

System.out.println(thread.getName());

當創(chuàng)建一個線程的時候,可以給線程起一個名字。它有助于我們區(qū)分不同的線程。

volatile

在多線程并發(fā)編程中synchronized和Volatile都扮演著重要的角色,Volatile是輕量級的synchronized,它在多處理器開發(fā)中保證了共享變量的“可見性”。可見性的意思是當一個線程修改一個共享變量時,另外一個線程能讀到這個修改的值。它在某些情況下比synchronized的開銷更小,但是volatile不能保證變量的原子性。

  volatile變量進行寫操作時(匯編下有l(wèi)ock指令),該lock指令在多核系統(tǒng)下有2個作用:

1、將當前CPU緩存行寫回系統(tǒng)內(nèi)存。

2、這個寫回操作會引起其他CPU緩存了改地址的數(shù)據(jù)失效。

多CPU下遵循緩存一致性原則,每個CPU通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己的緩存值是否過期了,當發(fā)現(xiàn)緩存對應(yīng)的內(nèi)存地址被修改,將對應(yīng)緩存行設(shè)置為無效狀態(tài),下次對數(shù)據(jù)操作會從系統(tǒng)內(nèi)存重新讀取。更多volatile知識請點擊深入分析Volatile的實現(xiàn)原理。

synchronized

在多線程并發(fā)編程中Synchronized一直是元老級角色,很多人都會稱呼它為重量級鎖,但是隨著Java SE1.6對Synchronized進行了各種優(yōu)化之后,有些情況下它并不那么重了。

  Java中每一個對象都可以作為鎖,當一個線程試圖訪問同步代碼塊時,它首先必須得到鎖,退出或拋出異常時必須釋放鎖。

對于同步方法,鎖是當前實例對象。

對于靜態(tài)同步方法,鎖是當前對象的Class對象。

對于同步方法塊,鎖是Synchonized括號里配置的對象。

synchronized關(guān)鍵字是不能繼承的,也就是說基類中的synchronized方法在子類中默認并不是synchronized的。當線程試圖訪問同步代碼塊時,必須先獲得鎖,退出或拋出異常時釋放鎖。Java中每個對象都可以作為鎖,那么鎖存在哪里呢?鎖存在Java對象頭中,如果對象是數(shù)組類型,則虛擬機用3個word(字寬) 存儲對象頭,如果對象是非數(shù)組類型,則用2字寬存儲對象頭。更多synchronized知識請點擊Java SE1.6中的Synchronized。

線程池

線程池負責(zé)管理工作線程,包含一個等待執(zhí)行的任務(wù)隊列。線程池的任務(wù)隊列是一個Runnable集合,工作線程負責(zé)從任務(wù)隊列中取出并執(zhí)行Runnable對象。

ExecutorService executor = Executors.newCachedThreadPool();

for(inti = 0; i < 5; i++) {

? ? executor.execute(new MyThread2());

}

executor.shutdown();

Java通過Executors提供了4種線程池:

1、newCachedThreadPool:創(chuàng)建一個可緩存線程池,對于新任務(wù)如果沒有空閑線程就新創(chuàng)建一個線程,如果空閑線程超過一定時間就會回收。

2、newFixedThreadPool:創(chuàng)建一個固定數(shù)量線程的線程池。

3、newSingleThreadExecutor:創(chuàng)建一個單線程的線程池,該線程池只用一個線程來執(zhí)行任務(wù),保證所有任務(wù)都按照FIFO順序執(zhí)行。

4、newScheduledThreadPool:創(chuàng)建一個定長線程池,支持定時及周期性任務(wù)執(zhí)行。

  以上幾種線程池底層都是調(diào)用ThreadPoolExecutor來創(chuàng)建線程池的

ThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime, TimeUnit unit, BlockingQueue workQueue)

1、corePoolSize(線程池的基本大?。寒斕峤灰粋€任務(wù)到線程池時,線程池會創(chuàng)建一個線程來執(zhí)行任務(wù),即使其他空閑的基本線程能夠執(zhí)行新任務(wù)也會創(chuàng)建線程,等到需要執(zhí)行的任務(wù)數(shù)大于線程池基本大小時就不再創(chuàng)建。如果調(diào)用了線程池的prestartAllCoreThreads方法,線程池會提前創(chuàng)建并啟動所有基本線程。

2、maximumPoolSize(線程池最大大?。壕€程池允許創(chuàng)建的最大線程數(shù)。如果隊列滿了,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù),則線程池會再創(chuàng)建新的線程執(zhí)行任務(wù)。值得注意的是如果使用了無界的任務(wù)隊列這個參數(shù)就沒什么效果。

3、keepAliveTime(線程活動保持時間):線程池的工作線程空閑后,保持存活的時間。所以如果任務(wù)很多,并且每個任務(wù)執(zhí)行的時間比較短,可以調(diào)大這個時間,提高線程的利用率。

4、TimeUnit(線程活動保持時間的單位):可選的單位有天(DAYS),小時(HOURS),分鐘(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。,可以選擇的阻塞隊列有以下幾種:

workQueue(任務(wù)隊列):用于保存等待執(zhí)行的任務(wù)的阻塞隊列。

ArrayBlockingQueue:是一個基于數(shù)組結(jié)構(gòu)的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。

LinkedBlockingQueue:一個基于鏈表結(jié)構(gòu)的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個隊列。

SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工廠方法Executors.newCachedThreadPool使用了這個隊列。

PriorityBlockingQueue:一個具有優(yōu)先級得無限阻塞隊列。

當提交新任務(wù)到線程池時,其處理流程如下:

1、先判斷基本線程池是否已滿?沒滿則創(chuàng)建一個工作線程來執(zhí)行任務(wù),滿了則進入下個流程。

2、其次判斷工作隊列是否已滿?沒滿則提交新任務(wù)到工作隊列中,滿了則進入下個流程。

3、最后判斷整個線程池是否已滿?沒滿則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù),滿了則交給飽和策略來處理這個任務(wù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文是我自己在秋招復(fù)習(xí)時的讀書筆記,整理的知識點,也是為了防止忘記,尊重勞動成果,轉(zhuǎn)載注明出處哦!如果你也喜歡,那...
    波波波先森閱讀 11,619評論 4 56
  • 進程和線程 進程 所有運行中的任務(wù)通常對應(yīng)一個進程,當一個程序進入內(nèi)存運行時,即變成一個進程.進程是處于運行過程中...
    勝浩_ae28閱讀 5,257評論 0 23
  • 一、wait--notify--sleep Object obj = new Object(); obj.wait...
    fe0180bd6eaf閱讀 391評論 0 1
  • 01 現(xiàn)在的我們似乎越來越不喜歡參加社交、聚餐、同學(xué)會了。 理由不外乎就幾個:懶、窮、沒時間和… 害怕。 害怕自己...
    栗九的酒閱讀 357評論 0 1
  • 昨天被叫到一間灰色的小屋,領(lǐng)導(dǎo)老Q問:你覺得我們的這個現(xiàn)狀有沒有需要改善的?你有什么看法或者想說的?! 醉翁之意不...
    天牙閱讀 877評論 2 0

友情鏈接更多精彩內(nèi)容