- N +

java多線程詳解(java多線程的運行與平臺相關)

大家好,今天給各位分享java多線程詳解的一些知識,其中也會對java多線程的運行與平臺相關進行解釋,文章篇幅可能偏長,如果能碰巧解決你現在面臨的問題,別忘了關注本站,現在就馬上開始吧!

Java大數據要學一些什么內容

這是一個非常好的問題,也是很多初學者比較關心的問題,作為一名IT從業者,我來回答一下。

首先,所謂的Java大數據通常指的是采用Java語言來完成一些大數據領域的開發任務,整體的學習內容涉及到三大塊,其一是Java語言基礎,其二是大數據平臺基礎,其三是場景開發基礎。總體上來說,Java大數據的學習內容是比較多的,而且也具有一定的難度。

java語言基礎部分的學習內容相對比較明確,由于Java語言本身的技術體系已經比較成熟了,所以學習過程也會相對比較順利。對于初學者來說,建議圍繞JavaWeb開發來制定學習計劃,這樣也會提升就業競爭力。JavaWeb開發不僅涉及到后端開發知識,還涉及到前端開發知識,整體的知識量還是比較大的,而且在學習的過程中,需要完成大量的實驗。

大數據平臺部分可以圍繞Hadoop來展開,由于當前Hadoop生態已經比較健全了,所以這部分學習內容非常多,需要初學者有一個系統的學習過程。學習Hadoop的初期是完全可以自學的,當前Hadoop的案例也越來越豐富了,所以學習Hadoop也會有一個較好的學習體驗。由于Hadoop對于實驗場景有一定的要求,所以搭建實驗環境是學習Hadoop的一個重要基礎。

學習Java大數據一定離不開具體的場景,這里面的場景不僅指硬件場景(數據中心),還需要有行業場景支持,所以學習Java大數據通常都會選擇一個行業作為切入點,比如金融行業、醫療行業、教育行業等等。初學者在學習場景開發知識的過程中,并不建議完全采用自學的學習方式,可以考慮在實習崗位上來完成這個階段的學習任務。

我從事互聯網行業多年,目前也在帶計算機專業的研究生,主要的研究方向集中在大數據和人工智能領域,我會陸續寫一些關于互聯網技術方面的文章,感興趣的朋友可以關注我,相信一定會有所收獲。

如果有互聯網、大數據、人工智能等方面的問題,或者是考研方面的問題,都可以在評論區留言,或者私信我!

java多線程為什么順序執行

thread類是被繼承的,執行的時候調用的是繼承它的子類,但java一般實現多線程不是繼承thread類,而是實現runnable接口,因為java不能多重繼承,所以繼承thread類后就不能繼承別的類了。

只要實現runnable接口(或繼承了thread類)就可以實現多線程。

比如說有abcde五個類都實現runnable接口(或繼承了thread類)你先進了main方法,就創建了一個線程,這個線程是main方法的你調用a的run()方法,就又創建一個線程,這個線程是a方法的。如果還不懂得話建議你去看看什么叫繼承和接口,基礎差的話理解起來有點困難我可是辛辛苦苦打字半天了~~~

java中如何保證多線程順序執行

謝邀。

首先java多線程的目的是為了并發執行而不是順序執行,如果多線程順序的話那就和單線程沒有區別。

但是“順序”還是有意義的,就是邏輯上要求幾個操作保證順序,這里就指的就是asifserial。即多線程大多數時候都是并發執行,但是在訪問同步資源時通過同步器讓線程順序,如synchronize,lock,aqs等。

同時在cpu指令層面也是同樣,指令重排序看起來是亂序執行,但在執行效果上和順序一樣。這些機制的初衷都是:讓程序執行的更快。

希望回答有幫助。

java多線程知識講解

對于Java編程的多線程知識,我們還是要了解很多的,首先我們要知道。java中的線程分為兩種:守護線程(Daemon)和用戶線程(User)。任何線程都可以設置為守護線程和用戶線程,通過方法Thread.setDaemon(boolon);true則把該線程設置為守護線程,反之則為用戶線程。

Thread.setDaemon()必須在Thread.start()之前調用,否則運行時會拋出異常。

Java多線程同步內部如何實現的

提示

請帶著這些問題繼續后文,會很大程度上幫助你更好的理解相關知識點。@pdai

為什么要有線程池?Java是實現和管理線程池有哪些方式?請簡單舉例如何使用。為什么很多公司不允許使用Executors去創建線程池?那么推薦怎么使用呢?ThreadPoolExecutor有哪些核心的配置參數?請簡要說明ThreadPoolExecutor可以創建哪是哪三種線程池呢?當隊列滿了并且worker的數量達到maxSize的時候,會怎么樣?說說ThreadPoolExecutor有哪些RejectedExecutionHandler策略?默認是什么策略?簡要說下線程池的任務執行機制?execute–>addWorker–>runworker(getTask)線程池中任務是如何提交的?線程池中任務是如何關閉的?在配置線程池的時候需要考慮哪些配置因素?如何監控線程池的狀態?為什么要有線程池

線程池能夠對線程進行統一分配,調優和監控:

降低資源消耗(線程無限制地創建,然后使用完畢后銷毀)提高響應速度(無須創建線程)提高線程的可管理性ThreadPoolExecutor例子

Java是如何實現和管理線程池的?

從JDK5開始,把工作單元與執行機制分離開來,工作單元包括Runnable和Callable,而執行機制由Executor框架提供。

WorkerThread

SimpleThreadPool

程序中我們創建了固定大小為五個工作線程的線程池。然后分配給線程池十個工作,因為線程池大小為五,它將啟動五個工作線程先處理五個工作,其他的工作則處于等待狀態,一旦有工作完成,空閑下來工作線程就會撿取等待隊列里的其他工作進行執行。

這里是以上程序的輸出。

輸出表明線程池中至始至終只有五個名為"pool-1-thread-1"到"pool-1-thread-5"的五個線程,這五個線程不隨著工作的完成而消亡,會一直存在,并負責執行分配給線程池的任務,直到線程池消亡。

Executors類提供了使用了ThreadPoolExecutor的簡單的ExecutorService實現,但是ThreadPoolExecutor提供的功能遠不止于此。我們可以在創建ThreadPoolExecutor實例時指定活動線程的數量,我們也可以限制線程池的大小并且創建我們自己的RejectedExecutionHandler實現來處理不能適應工作隊列的工作。

這里是我們自定義的RejectedExecutionHandler接口的實現。

RejectedExecutionHandlerImpl.java

ThreadPoolExecutor提供了一些方法,我們可以使用這些方法來查詢executor的當前狀態,線程池大小,活動線程數量以及任務數量。因此我是用來一個監控線程在特定的時間間隔內打印executor信息。

MyMonitorThread.java

這里是使用ThreadPoolExecutor的線程池實現例子。

WorkerPool.java

注意在初始化ThreadPoolExecutor時,我們保持初始池大小為2,最大池大小為4而工作隊列大小為2。因此如果已經有四個正在執行的任務而此時分配來更多任務的話,工作隊列將僅僅保留他們(新任務)中的兩個,其他的將會被RejectedExecutionHandlerImpl處理。

上面程序的輸出可以證實以上觀點。

注意executor的活動任務、完成任務以及所有完成任務,這些數量上的變化。我們可以調用shutdown()方法來結束所有提交的任務并終止線程池。

ThreadPoolExecutor使用詳解

其實java線程池的實現原理很簡單,說白了就是一個線程集合workerSet和一個阻塞隊列workQueue。當用戶向線程池提交一個任務(也就是線程)時,線程池會先將任務放入workQueue中。workerSet中的線程會不斷的從workQueue中獲取線程然后執行。當workQueue中沒有任務的時候,worker就會阻塞,直到隊列中有任務了就取出來繼續執行。

Execute原理

當一個任務提交至線程池之后:

線程池首先當前運行的線程數量是否少于corePoolSize。如果是,則創建一個新的工作線程來執行任務。如果都在執行任務,則進入2.判斷BlockingQueue是否已經滿了,倘若還沒有滿,則將線程放入BlockingQueue。否則進入3.如果創建一個新的工作線程將使當前運行的線程數量超過maximumPoolSize,則交給RejectedExecutionHandler來處理任務。

當ThreadPoolExecutor創建新線程時,通過CAS來更新線程池的狀態ctl.

參數corePoolSize線程池中的核心線程數,當提交一個任務時,線程池創建一個新線程執行任務,直到當前線程數等于corePoolSize,即使有其他空閑線程能夠執行新來的任務,也會繼續創建線程;如果當前線程數為corePoolSize,繼續提交的任務被保存到阻塞隊列中,等待被執行;如果執行了線程池的prestartAllCoreThreads()方法,線程池會提前創建并啟動所有核心線程。workQueue用來保存等待被執行的任務的阻塞隊列.在JDK中提供了如下阻塞隊列:具體可以參考JUC集合:BlockQueue詳解ArrayBlockingQueue:基于數組結構的有界阻塞隊列,按FIFO排序任務;LinkedBlockingQueue:基于鏈表結構的阻塞隊列,按FIFO排序任務,吞吐量通常要高于ArrayBlockingQueue;SynchronousQueue:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處于阻塞狀態,吞吐量通常要高于LinkedBlockingQueue;PriorityBlockingQueue:具有優先級的無界阻塞隊列;

LinkedBlockingQueue比ArrayBlockingQueue在插入刪除節點性能方面更優,但是二者在put(),take()任務的時均需要加鎖,SynchronousQueue使用無鎖算法,根據節點的狀態判斷執行,而不需要用到鎖,其核心是Transfer.transfer().

maximumPoolSize線程池中允許的最大線程數。如果當前阻塞隊列滿了,且繼續提交任務,則創建新的線程執行任務,前提是當前線程數小于maximumPoolSize;當阻塞隊列是無界隊列,則maximumPoolSize則不起作用,因為無法提交至核心線程池的線程會一直持續地放入workQueue.keepAliveTime線程空閑時的存活時間,即當線程沒有任務執行時,該線程繼續存活的時間;默認情況下,該參數只在線程數大于corePoolSize時才有用,超過這個時間的空閑線程將被終止;unitkeepAliveTime的單位threadFactory創建線程的工廠,通過自定義的線程工廠可以給每個新建的線程設置一個具有識別度的線程名。默認為DefaultThreadFactoryhandler線程池的飽和策略,當阻塞隊列滿了,且沒有空閑的工作線程,如果繼續提交任務,必須采取一種策略處理該任務,線程池提供了4種策略:AbortPolicy:直接拋出異常,默認策略;CallerRunsPolicy:用調用者所在的線程來執行任務;DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,并執行當前任務;DiscardPolicy:直接丟棄任務;

當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日志或持久化存儲不能處理的任務。

三種類型newFixedThreadPool

線程池的線程數量達corePoolSize后,即使線程池沒有可執行任務時,也不會釋放線程。

FixedThreadPool的工作隊列為無界隊列LinkedBlockingQueue(隊列容量為Integer.MAX_VALUE),這會導致以下問題:

線程池里的線程數量不超過corePoolSize,這導致了maximumPoolSize和keepAliveTime將會是個無用參數由于使用了無界隊列,所以FixedThreadPool永遠不會拒絕,即飽和策略失效newSingleThreadExecutor

初始化的線程池中只有一個線程,如果該線程異常結束,會重新創建一個新的線程繼續執行任務,唯一的線程可以保證所提交任務的順序執行.

由于使用了無界隊列,所以SingleThreadPool永遠不會拒絕,即飽和策略失效

newCachedThreadPool

線程池的線程數可達到Integer.MAX_VALUE,即2147483647,內部使用SynchronousQueue作為阻塞隊列;和newFixedThreadPool創建的線程池不同,newCachedThreadPool在沒有任務執行時,當線程的空閑時間超過keepAliveTime,會自動釋放線程資源,當提交新任務時,如果沒有空閑線程,則創建新線程執行任務,會導致一定的系統開銷;執行過程與前兩種稍微不同:

主線程調用SynchronousQueue的offer()方法放入task,倘若此時線程池中有空閑的線程嘗試讀取SynchronousQueue的task,即調用了SynchronousQueue的poll(),那么主線程將該task交給空閑線程.否則執行(2)當線程池為空或者沒有空閑的線程,則創建新的線程執行任務.執行完任務的線程倘若在60s內仍空閑,則會被終止.因此長時間空閑的CachedThreadPool不會持有任何線程資源.關閉線程池

遍歷線程池中的所有線程,然后逐個調用線程的interrupt方法來中斷線程.

關閉方式-shutdown

將線程池里的線程狀態設置成SHUTDOWN狀態,然后中斷所有沒有正在執行任務的線程.

關閉方式-shutdownNow

將線程池里的線程狀態設置成STOP狀態,然后停止所有正在執行或暫停任務的線程.只要調用這兩個關閉方法中的任意一個,isShutDown()返回true.當所有任務都成功關閉了,isTerminated()返回true.

ThreadPoolExecutor源碼詳解幾個關鍵屬性內部狀態

其中AtomicInteger變量ctl的功能非常強大:利用低29位表示線程池中線程數,通過高3位表示線程池的運行狀態:

RUNNING:-1<<COUNT_BITS,即高3位為111,該狀態的線程池會接收新任務,并處理阻塞隊列中的任務;SHUTDOWN:0<<COUNT_BITS,即高3位為000,該狀態的線程池不會接收新任務,但會處理阻塞隊列中的任務;STOP:1<<COUNT_BITS,即高3位為001,該狀態的線程不會接收新任務,也不會處理阻塞隊列中的任務,而且會中斷正在運行的任務;TIDYING:2<<COUNT_BITS,即高3位為010,所有的任務都已經終止;TERMINATED:3<<COUNT_BITS,即高3位為011,terminated()方法已經執行完成任務的執行

execute–>addWorker–>runworker(getTask)

線程池的工作線程通過Woker類實現,在ReentrantLock鎖的保證下,把Woker實例插入到HashSet后,并啟動Woker中的線程。從Woker類的構造方法實現可以發現:線程工廠在創建線程thread時,將Woker實例本身this作為參數傳入,當執行start方法啟動線程thread時,本質是執行了Worker的runWorker方法。firstTask執行完成之后,通過getTask方法從阻塞隊列中獲取等待的任務,如果隊列中沒有任務,getTask方法會被阻塞并掛起,不會占用cpu資源;

execute()方法

ThreadPoolExecutor.execute(task)實現了Executor.execute(task)

為什么需要doublecheck線程池的狀態?

在多線程環境下,線程池的狀態時刻在變化,而ctl.get()是非原子操作,很有可能剛獲取了線程池狀態后線程池狀態就改變了。判斷是否將command加入workque是線程池之前的狀態。倘若沒有doublecheck,萬一線程池處于非running狀態(在多線程環境下很有可能發生),那么command永遠不會執行。

addWorker方法

從方法execute的實現可以看出:addWorker主要負責創建新的線程并執行任務線程池創建新線程執行任務時,需要獲取全局鎖:

Worker類的runworker方法繼承了AQS類,可以方便的實現工作線程的中止操作;實現了Runnable接口,可以將自身作為一個任務在工作線程中執行;當前提交的任務firstTask作為參數傳入Worker的構造方法;

一些屬性還有構造方法:

runWorker方法是線程池的核心:

線程啟動之后,通過unlock方法釋放鎖,設置AQS的state為0,表示運行可中斷;Worker執行firstTask或從workQueue中獲取任務:進行加鎖操作,保證thread不被其他線程中斷(除非線程池被中斷)檢查線程池狀態,倘若線程池處于中斷狀態,當前線程將中斷。執行beforeExecute執行任務的run方法執行afterExecute方法解鎖操作

通過getTask方法從阻塞隊列中獲取等待的任務,如果隊列中沒有任務,getTask方法會被阻塞并掛起,不會占用cpu資源;

getTask方法

下面來看一下getTask()方法,這里面涉及到keepAliveTime的使用,從這個方法我們可以看出線程池是怎么讓超過corePoolSize的那部分worker銷毀的。

注意這里一段代碼是keepAliveTime起作用的關鍵:

allowCoreThreadTimeOut為false,線程即使空閑也不會被銷毀;倘若為ture,在keepAliveTime內仍空閑則會被銷毀。

如果線程允許空閑等待而不被銷毀timed==false,workQueue.take任務:如果阻塞隊列為空,當前線程會被掛起等待;當隊列中有任務加入時,線程被喚醒,take方法返回任務,并執行;

如果線程不允許無休止空閑timed==true,workQueue.poll任務:如果在keepAliveTime時間內,阻塞隊列還是沒有任務,則返回null;

任務的提交submit任務,等待線程池execute執行FutureTask類的get方法時,會把主線程封裝成WaitNode節點并保存在waiters鏈表中,并阻塞等待運行結果;FutureTask任務執行完成后,通過UNSAFE設置waiters相應的waitNode為null,并通過LockSupport類unpark方法喚醒主線程;

在實際業務場景中,Future和Callable基本是成對出現的,Callable負責產生結果,Future負責獲取結果。

Callable接口類似于Runnable,只是Runnable沒有返回值。Callable任務除了返回正常結果之外,如果發生異常,該異常也會被返回,即Future可以拿到異步執行任務各種結果;Future.get方法會導致主線程阻塞,直到Callable任務執行完成;submit方法

AbstractExecutorService.submit()實現了ExecutorService.submit()可以獲取執行完的返回值,而ThreadPoolExecutor是AbstractExecutorService.submit()的子類,所以submit方法也是ThreadPoolExecutor`的方法。

通過submit方法提交的Callable任務會被封裝成了一個FutureTask對象。通過Executor.execute方法提交FutureTask到線程池中等待被執行,最終執行的是FutureTask的run方法;

FutureTask對象

publicclassFutureTask<V>implementsRunnableFuture<V>可以將FutureTask提交至線程池中等待被執行(通過FutureTask的run方法來執行)

內部狀態

內部狀態的修改通過sun.misc.Unsafe修改

get方法

內部通過awaitDone方法對主線程進行阻塞,具體實現如下:

如果主線程被中斷,則拋出中斷異常;

判斷FutureTask當前的state,如果大于COMPLETING,說明任務已經執行完成,則直接返回;如果當前state等于COMPLETING,說明任務已經執行完,這時主線程只需通過yield方法讓出cpu資源,等待state變成NORMAL;通過WaitNode類封裝當前線程,并通過UNSAFE添加到waiters鏈表;最終通過LockSupport的park或parkNanos掛起線程;

run方法

FutureTask.run方法是在線程池中被執行的,而非主線程

通過執行Callable任務的call方法;如果call執行成功,則通過set方法保存結果;如果call執行有異常,則通過setException保存異常;任務的關閉

shutdown方法會將線程池的狀態設置為SHUTDOWN,線程池進入這個狀態后,就拒絕再接受任務,然后會將剩余的任務全部執行完

shutdownNow做的比較絕,它先將線程池狀態設置為STOP,然后拒絕所有提交的任務。最后中斷左右正在運行中的worker,然后清空任務隊列。

更深入理解

為什么線程池不允許使用Executors去創建?推薦方式是什么?

線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。說明:Executors各個方法的弊端:

newFixedThreadPool和newSingleThreadExecutor:??主要問題是堆積的請求處理隊列可能會耗費非常大的內存,甚至OOM。newCachedThreadPool和newScheduledThreadPool:??主要問題是線程數最大數是Integer.MAX_VALUE,可能會創建數量非常多的線程,甚至OOM。推薦方式1

首先引入:commons-lang3包

推薦方式2

首先引入:com.google.guava包

推薦方式3

spring配置線程池方式:自定義線程工廠bean需要實現ThreadFactory,可參考該接口的其它默認實現類,使用方式直接注入bean調用execute(Runnabletask)方法即可

配置線程池需要考慮因素

從任務的優先級,任務的執行時間長短,任務的性質(CPU密集/IO密集),任務的依賴關系這四個角度來分析。并且近可能地使用有界的工作隊列。

性質不同的任務可用使用不同規模的線程池分開處理:

CPU密集型:盡可能少的線程,Ncpu+1IO密集型:盡可能多的線程,Ncpu*2,比如數據庫連接池混合型:CPU密集型的任務與IO密集型任務的執行時間差別較小,拆分為兩個線程池;否則沒有必要拆分。監控線程池的狀態

可以使用ThreadPoolExecutor以下方法:

getTaskCount()Returnstheapproximatetotalnumberoftasksthathaveeverbeenscheduledforexecution.getCompletedTaskCount()Returnstheapproximatetotalnumberoftasksthathavecompletedexecution.返回結果少于getTaskCount()。getLargestPoolSize()Returnsthelargestnumberofthreadsthathaveeversimultaneouslybeeninthepool.返回結果小于等于maximumPoolSizegetPoolSize()Returnsthecurrentnumberofthreadsinthepool.getActiveCount()Returnstheapproximatenumberofthreadsthatareactivelyexecutingtasks.參考文章《Java并發編程藝術》https://www.jianshu.com/p/87bff5cc8d8chttps://blog.csdn.net/programmer_at/article/details/79799267https://blog.csdn.net/u013332124/article/details/79587436https://www.journaldev.com/1069/threadpoolexecutor-java-thread-pool-example-executorservice

由于問答代碼塊插入受限,部分代碼未完全展示,若有需要可閱讀原文:戳我閱讀原文

好了,文章到這里就結束啦,如果本次分享的java多線程詳解和java多線程的運行與平臺相關問題對您有所幫助,還望關注下本站哦!

返回列表
上一篇:
下一篇: