大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

面試題首頁(yè) > 多線程面試題

多線程面試題

001為什么要使用多線程呢?

我們現(xiàn)在所使用操作系統(tǒng)都是多任務(wù)操作系統(tǒng)(早期使用的DOS操作系統(tǒng)為單任務(wù)操作系統(tǒng)),多任務(wù)操作指在同一時(shí)刻可以同時(shí)做多件事(可以同時(shí)執(zhí)行多個(gè)程序)。
多進(jìn)程:每個(gè)程序都是一個(gè)進(jìn)程,在操作系統(tǒng)中可以同時(shí)執(zhí)行多個(gè)程序,多進(jìn)程的目的是為了有效的使用CPU資源,每開一個(gè)進(jìn)程系統(tǒng)要為該進(jìn)程分配相關(guān)的系統(tǒng)資源(內(nèi)存資源);
多線程:線程是進(jìn)程內(nèi)部比進(jìn)程更小的執(zhí)行單元(執(zhí)行流|程序片段),每個(gè)線程完成一個(gè)任務(wù),每個(gè)進(jìn)程內(nèi)部包含了多個(gè)線程每個(gè)線程做自己的事情,在進(jìn)程中的所有線程共享該進(jìn)程的資源;
主線程:在進(jìn)程中至少存在一個(gè)主線程,其他子線程都由主線程開啟,主線程不一定在其他線程結(jié)束后結(jié)束,有可能在其他線程結(jié)束前結(jié)束。Java中的主線程是main線程,是Java的main函數(shù);

002多線程應(yīng)用場(chǎng)景?

當(dāng)應(yīng)用場(chǎng)景為計(jì)算密集型時(shí):為了將每個(gè)cpu充分利用起來(lái),線程數(shù)量正常是cpu核數(shù)+1,還可以看jdk的使用版本,1.8版本中可以使用cpu核數(shù)*2。
當(dāng)應(yīng)用場(chǎng)景為io密集型時(shí):做web端開發(fā)的時(shí)候,涉及到大量的網(wǎng)絡(luò)傳輸,不進(jìn)入持,緩存和與數(shù)據(jù)庫(kù)交互也會(huì)存在大量io,當(dāng)發(fā)生io時(shí)候,線程就會(huì)停止,等待io結(jié)束,數(shù)據(jù)準(zhǔn)備好,線程才會(huì)繼續(xù)執(zhí)行,所以當(dāng)io密集時(shí),可以多創(chuàng)建點(diǎn)線程,讓線程等待時(shí)候,其他線程執(zhí)行,更高效的利用cpu效率,他有一個(gè)計(jì)算公式,套用公式的話,雙核cpu理想的線程數(shù)就是20。

003多線程的好處?

采用多線程技術(shù)的應(yīng)用程序可以更好地利用系統(tǒng)資源。主要優(yōu)勢(shì)在于充分利用了CPU的空閑時(shí)間片,用盡可能少的時(shí)間來(lái)對(duì)用戶的要求做出響應(yīng),使得進(jìn)程的整體運(yùn)行效率得到較大提高,同時(shí)增強(qiáng)了應(yīng)用程序的靈活性。由于同一進(jìn)程的所有線程是共享同一內(nèi)存,所以不需要特殊的數(shù)據(jù)傳送機(jī)制,不需要建立共享存儲(chǔ)區(qū)或共享文件,從而使得不同任務(wù)之間的協(xié)調(diào)操作與運(yùn)行、數(shù)據(jù)的交互、資源的分配等問題更加易于解決。

004使用多線程可能帶來(lái)什么問題?

多線程的目的就是為了能提高程序的執(zhí)行效率提高程序運(yùn)行速度,但是并發(fā)編程并不總是能提高程序運(yùn)行速度的,而且并發(fā)編程可能會(huì)遇到很多問題,比如:內(nèi)存泄漏、死鎖、線程不安全等等。

005線程和進(jìn)程的區(qū)別?

進(jìn)程:是正在運(yùn)行中的程序,是系統(tǒng)進(jìn)行資源調(diào)度和分配的的基本單位。
線程:是進(jìn)程的子任務(wù),是任務(wù)調(diào)度和執(zhí)行的基本單位;
一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程,線程依賴于進(jìn)程而存在;
進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享進(jìn)程的內(nèi)存。線程與進(jìn)程相似,但線程是一個(gè)比進(jìn)程更小的執(zhí)行單位。
一個(gè)進(jìn)程在其執(zhí)行的過程中可以產(chǎn)生多個(gè)線程。與進(jìn)程不同的是同類的多個(gè)線程共享進(jìn)程的堆和方法區(qū)資源,但每個(gè)線程有自己的程序計(jì)數(shù)器、虛擬機(jī)棧和本地方法棧,所以系統(tǒng)在產(chǎn)生一個(gè)線程,或是在各個(gè)線程之間作切換工作時(shí),負(fù)擔(dān)要比進(jìn)程小得多,也正因?yàn)槿绱耍€程也被稱為輕量級(jí)進(jìn)程。

006創(chuàng)建線程的幾種方式?

a.繼承 Thread 類;b.實(shí)現(xiàn) Runnable 接口;c. 實(shí)現(xiàn)Callable接口;d. 使用線程池。

007創(chuàng)建線程用Runnable還是Thread?

我們可以通過繼承Thread類或者調(diào)用Runnable接口來(lái)實(shí)現(xiàn)線程,因?yàn)镴ava不支持類的多重繼承,但允許你調(diào)用多個(gè)接口。所以如果你想要繼承其他的類,當(dāng)然是調(diào)用Runnable接口好了。

008實(shí)現(xiàn)Runnable接口和Callable接口的區(qū)別?

如果想讓線程池執(zhí)行任務(wù)的話需要實(shí)現(xiàn)的Runnable接口或Callable接口。 Runnable接口或Callable接口實(shí)現(xiàn)類都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor執(zhí)行。兩者的區(qū)別在于 Runnable 接口不會(huì)返回結(jié)果但是 Callable 接口可以返回結(jié)果。

009線程的 run()和 start()有什么區(qū)別?

啟動(dòng)一個(gè)線程需要調(diào)用 Thread 對(duì)象的 start() 方法;
調(diào)用線程的 start() 方法后,線程處于可運(yùn)行狀態(tài),此時(shí)它可以由 JVM 調(diào)度并執(zhí)行,這并不意味著線程就會(huì)立即運(yùn)行;
run() 方法是線程運(yùn)行時(shí)由 JVM 回調(diào)的方法,無(wú)需手動(dòng)寫代碼調(diào)用;
直接調(diào)用線程的 run() 方法,相當(dāng)于在調(diào)用線程里繼續(xù)調(diào)用了一個(gè)普通的方法,并未啟動(dòng)一個(gè)新的線程。

010說說線程的生命周期和狀態(tài)?

線程通常有五種狀態(tài):創(chuàng)建,就緒,運(yùn)行,阻塞和死亡狀態(tài)
(1)創(chuàng)建狀態(tài)(New):新創(chuàng)建了一個(gè)線程對(duì)象。
(2)就緒狀態(tài)(Runnable):線程對(duì)象創(chuàng)建后,其他線程調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,變得可運(yùn)行,等待獲取CPU的使用權(quán)。
(3)運(yùn)行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼。
(4)阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行。直到線程進(jìn)入就緒狀態(tài),才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:
(一)等待阻塞:運(yùn)行的線程執(zhí)行wait()方法,JVM會(huì)把該線程放入等待池中。(wait會(huì)釋放持有的鎖)
(二)同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線程占用,則JVM會(huì)把該線程放入鎖池中。
(三)其他阻塞:運(yùn)行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。(注意,sleep是不會(huì)釋放持有的鎖)
(5)死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。

011什么是上下文切換?

多線程編程中一般線程的個(gè)數(shù)都大于 CPU 核心的個(gè)數(shù),而一個(gè) CPU 核心在任意時(shí)刻只能被一個(gè)線程使用,為了讓這些線程都能得到有效執(zhí)行,CPU 采取的策略是為每個(gè)線程分配時(shí)間片并輪轉(zhuǎn)的形式。當(dāng)一個(gè)線程的時(shí)間片用完的時(shí)候就會(huì)重新處于就緒狀態(tài)讓給其他線程使用,這個(gè)過程就屬于一次上下文切換。
概括來(lái)說就是:當(dāng)前任務(wù)在執(zhí)行完 CPU 時(shí)間片切換到另一個(gè)任務(wù)之前會(huì)先保存自己的狀態(tài),以便下次再切換回這個(gè)任務(wù)時(shí),可以再加載這個(gè)任務(wù)的狀態(tài)。任務(wù)從保存到再加載的過程就是一次上下文切換。
上下文切換通常是計(jì)算密集型的。也就是說,它需要相當(dāng)可觀的處理器時(shí)間,在每秒幾十上百次的切換中,每次切換都需要納秒量級(jí)的時(shí)間。所以,上下文切換對(duì)系統(tǒng)來(lái)說意味著消耗大量的 CPU 時(shí)間,事實(shí)上,可能是操作系統(tǒng)中時(shí)間消耗最大的操作。
Linux 相比與其他操作系統(tǒng)(包括其他類 Unix 系統(tǒng))有很多的優(yōu)點(diǎn),其中有一項(xiàng)就是,其上下文切換和模式切換的時(shí)間消耗非常少。

012如何創(chuàng)建守護(hù)線程?

使用Thread類的setDaemon(true)方法可以將線程設(shè)置為守護(hù)線程,需要注意的是,需要在調(diào)用start()方法前調(diào)用這個(gè)方法,否則會(huì)拋出IllegalThreadStateException異常。

013用戶線程和守護(hù)線程有什么區(qū)別?

當(dāng)我們?cè)贘ava程序中創(chuàng)建一個(gè)線程,它就被稱為用戶線程。一個(gè)守護(hù)線程是在后臺(tái)執(zhí)行并且不會(huì)阻止JVM終止的線程。當(dāng)沒有用戶線程在運(yùn)行的時(shí)候,JVM關(guān)閉程序并且退出。一個(gè)守護(hù)線程創(chuàng)建的子線程依然是守護(hù)線程。

014為什么Thread類的sleep()和yield()方法是靜態(tài)的?

Thread類的sleep()和yield()方法將在當(dāng)前正在執(zhí)行的線程上運(yùn)行。所以在其他處于等待狀態(tài)的線程上調(diào)用這些方法是沒有意義的。這就是為什么這些方法是靜態(tài)的。它們可以在當(dāng)前正在執(zhí)行的線程中工作,并避免程序員錯(cuò)誤的認(rèn)為可以在其他非運(yùn)行線程調(diào)用這些方法。

015如何在 Windows 和 Linux 上查找哪個(gè)線程cpu利用率最高?

Windows系統(tǒng)下執(zhí)行java  -jar  arthas-boot.jar
Linux系統(tǒng)下解壓arthas,執(zhí)行ps  -ef  |  grep  java找出java進(jìn)程pid數(shù)字

016為什么我們調(diào)用 start() 方法時(shí)會(huì)執(zhí)行 run() 方法,為什么我們不能直接調(diào)用 run() 方法?

new 一個(gè) Thread,線程進(jìn)入了新建狀態(tài);調(diào)用 start() 方法,會(huì)啟動(dòng)一個(gè)線程并使線程進(jìn)入了就緒狀態(tài),當(dāng)分配到時(shí)間片后就可以開始運(yùn)行了。 start() 會(huì)執(zhí)行線程的相應(yīng)準(zhǔn)備工作,然后自動(dòng)執(zhí)行 run() 方法的內(nèi)容,這是真正的多線程工作。 而直接執(zhí)行 run() 方法,會(huì)把 run 方法當(dāng)成一個(gè) main 線程下的普通方法去執(zhí)行,并不會(huì)在某個(gè)線程中執(zhí)行它,所以這并不是多線程工作。
總結(jié): 調(diào)用 start 方法方可啟動(dòng)線程并使線程進(jìn)入就緒狀態(tài),而 run 方法只是 thread 的一個(gè)普通方法調(diào)用,還是在主線程里執(zhí)行.

017什么是 Callable 和 Future?

Callable接口類似于Runnable,從名字就可以看出來(lái),但是Runnable不會(huì)返回結(jié)果,并且無(wú)法拋出返回結(jié)果的異常,而Callable功能更強(qiáng)大一些,被線程執(zhí)行后,可以返回值,這個(gè)返回值可以被Future拿到,也就是說,F(xiàn)uture可以拿到異步執(zhí)行任務(wù)的返回值。可以認(rèn)為是帶有返回值的Runnable.Future接口表示異步任務(wù),是還沒有完成的任務(wù)給出的未來(lái)結(jié)果。所以說Callable用于產(chǎn)生結(jié)果,F(xiàn)uture用于獲取結(jié)果。

018線程調(diào)度策略有哪些?

(1) 搶占式調(diào)度策略
Java運(yùn)行時(shí)系統(tǒng)的線程調(diào)度算法是搶占式的 (preemptive)。Java運(yùn)行時(shí)系統(tǒng)支持一種簡(jiǎn)單的固定優(yōu)先級(jí)的調(diào)度算法。如果一個(gè)優(yōu)先級(jí)比其他任何處于可運(yùn)行狀態(tài)的線程都高的線程進(jìn)入就緒狀態(tài),那么運(yùn)行時(shí)系統(tǒng)就會(huì)選擇該線程運(yùn)行。新的優(yōu)先級(jí)較高的線程搶占(preempt)了其他線程。但是Java運(yùn)行時(shí)系統(tǒng)并不搶占同優(yōu)先級(jí)的線程。換句話說,Java運(yùn)行時(shí)系統(tǒng)不是分時(shí)的(time-slice)。然而,基于Java Thread類的實(shí)現(xiàn)系統(tǒng)可能是支持分時(shí)的,因此編寫代碼時(shí)不要依賴分時(shí)。當(dāng)系統(tǒng)中的處于就緒狀態(tài)的線程都具有相同優(yōu)先級(jí)時(shí),線程調(diào)度程序采用一種簡(jiǎn)單的、非搶占式的輪轉(zhuǎn)的調(diào)度順序。
(2) 時(shí)間片輪轉(zhuǎn)調(diào)度策略
有些系統(tǒng)的線程調(diào)度采用時(shí)間片輪轉(zhuǎn)(round-robin)調(diào)度策略。這種調(diào)度策略是從所有處于就緒狀態(tài)的線程中選擇優(yōu)先級(jí)最高的線程分配一定的CPU時(shí)間運(yùn)行。該時(shí)間過后再選擇其他線程運(yùn)行。只有當(dāng)線程運(yùn)行結(jié)束、放棄(yield)CPU或由于某種原因進(jìn)入阻塞狀態(tài),低優(yōu)先級(jí)的線程才有機(jī)會(huì)執(zhí)行。如果有兩個(gè)優(yōu)先級(jí)相同的線程都在等待CPU,則調(diào)度程序以輪轉(zhuǎn)的方式選擇運(yùn)行的線程。

019Java中用到的線程調(diào)度算法是什么?

搶占式。一個(gè)線程用完CPU之后,操作系統(tǒng)會(huì)根據(jù)線程優(yōu)先級(jí)、線程饑餓情況等數(shù)據(jù)算出一個(gè)總的優(yōu)先級(jí)并分配下一個(gè)時(shí)間片給某個(gè)線程執(zhí)行。

020什么是線程調(diào)度器(Thread Scheduler)和時(shí)間分片(Time Slicing )?

線程調(diào)度器是一個(gè)操作系統(tǒng)服務(wù),它負(fù)責(zé)為 Runnable 狀態(tài)的線程分配 CPU 時(shí)間。一旦我們創(chuàng)建一個(gè)線程并啟動(dòng)它,它的執(zhí)行便依賴于線程調(diào)度器的實(shí)現(xiàn)。同上一個(gè)問題,線程調(diào)度并不受到 Java 虛擬機(jī)控制,所以由應(yīng)用程序來(lái)控制它是 更好的選擇(也就是說不要讓你的程序依賴于線程的優(yōu)先級(jí))。時(shí)間分片是指將可用的 CPU 時(shí)間分配給可用的 Runnable 線程的過程。分配 CPU 時(shí)間可以基于線程優(yōu)先級(jí)或者線程等待的時(shí)間。 

021說說 sleep() 方法和 wait() 方法區(qū)別和共同點(diǎn)?

兩者最主要的區(qū)別在于:sleep 方法沒有釋放鎖,而 wait 方法釋放了鎖 。
兩者都可以暫停線程的執(zhí)行。
wait 通常被用于線程間交互/通信,sleep 通常被用于暫停執(zhí)行。
wait() 方法被調(diào)用后,線程不會(huì)自動(dòng)蘇醒,需要?jiǎng)e的線程調(diào)用同一個(gè)對(duì)象上的 notify() 或者 notifyAll() 方法。sleep() 方法執(zhí)行完成后,線程會(huì)自動(dòng)蘇醒。或者可以使用 wait(long timeout)超時(shí)后線程會(huì)自動(dòng)蘇醒。

022為什么線程通信的方法 wait(), notify()和 notifyAll()被定義在 Object 類里?

因?yàn)檫@些方法的調(diào)用是依賴鎖對(duì)象,而同步代碼塊的鎖對(duì)象是任意。鎖而Object代表任意的對(duì)象,所以定義在這里面。

023Thread 類中的 yield 方法有什么作用?

使當(dāng)前線程從執(zhí)行狀態(tài)(運(yùn)行狀態(tài))變?yōu)榭蓤?zhí)行態(tài)(就緒狀態(tài))。當(dāng)前線程到了就緒狀態(tài),那么接下來(lái)具體是哪個(gè)個(gè)線程會(huì)從就緒狀態(tài)變成執(zhí)行狀態(tài)就要看系統(tǒng)的分配了。

024為什么 Thread 類的 sleep()和 yield ()方法是靜態(tài)的?

Thread類的sleep()和yield()方法將在當(dāng)前正在執(zhí)行的線程上運(yùn)行。所以在其他處于等待狀態(tài)的線程上調(diào)用這些方法是沒有意義的。這就是為什么這些方法是靜態(tài)的。它們可以在當(dāng)前正在執(zhí)行的線程中工作,并避免程序員錯(cuò)誤的認(rèn)為可以在其他非運(yùn)行線程調(diào)用這些方法。

025線程的 sleep()方法和 yield()方法有什么區(qū)別?

Thread類的sleep()和yield()方法將在當(dāng)前正在執(zhí)行的線程上運(yùn)行。所以在其他處于等待狀態(tài)的線程上調(diào)用這些方法是沒有意義的。這就是為什么這些方法是靜態(tài)的。它們可以在當(dāng)前正在執(zhí)行的線程中工作,并避免程序員錯(cuò)誤的認(rèn)為可以在其他非運(yùn)行線程調(diào)用這些方法。

026多線程的中斷是什么?

線程在運(yùn)行過程中,有些時(shí)候可能需要中斷一些阻塞的線程,類Thread中提供了幾種中斷線程的方法,其中Thread.suspend()和Thread.stop()方法已經(jīng)過時(shí)了,因?yàn)檫@兩個(gè)方法是不安全的。Thread.stop(),會(huì)直接終止該線程,并且會(huì)立即釋放這個(gè)線程持有的所有鎖,而這些鎖恰恰是用來(lái)維持?jǐn)?shù)據(jù)的一致性的,如果此時(shí)。寫線程寫入數(shù)據(jù)時(shí)寫到一半,并強(qiáng)行終止,由于此時(shí)對(duì)象鎖已經(jīng)被釋放,另一個(gè)等待該鎖的讀線程就會(huì)讀到這個(gè)不一致的對(duì)象。Thread.suspend()會(huì)導(dǎo)致死鎖,Thread.resume()也不能使用。

027如何停止一個(gè)正在運(yùn)行的線程?

Java 提供了很豐富的API 但沒有為停止線程提供 API。JDK 1.0 本來(lái)有一些像stop(), suspend() 和 resume()的控制方法但是由于潛在的死鎖威脅因此在后續(xù)的JDK 版本中他們被棄用了.之后Java API 的設(shè)計(jì)者就沒有提供一個(gè)兼容且線程安全的方法來(lái)停止一個(gè)線程。當(dāng)run() 或者 call() 方法執(zhí)行完的時(shí)候線程會(huì)自動(dòng)結(jié)束, 如果要手動(dòng)結(jié)束一個(gè)線程.你可以用volatile 布爾變量來(lái)退出 run()方法的循環(huán)或者是取消任務(wù)來(lái)中斷線程。

028Java 中 interrupted 和 isInterrupted 方法的區(qū)別?

interrupted:查詢當(dāng)前線程的中斷狀態(tài),并且清除原狀態(tài)。如果一個(gè)線程被中斷了,第一次調(diào)用 interrupted 則返回 true,第二次和后面的就返回 false 了。
isInterrupted僅僅是查詢當(dāng)前線程的中斷狀態(tài)。

029notify() 和 notifyAll() 有什么區(qū)別?

1)notify只會(huì)隨機(jī)選取一個(gè)處于等待池中的線程進(jìn)入鎖池去競(jìng)爭(zhēng)獲取鎖的機(jī)會(huì);
2)notifyAll會(huì)讓所有處于等待池的線程全部進(jìn)入鎖池去競(jìng)爭(zhēng)獲取鎖的機(jī)會(huì);

030你對(duì)線程優(yōu)先級(jí)的理解是什么?

每一個(gè)線程都是有優(yōu)先級(jí)的.一般來(lái)說.高優(yōu)先級(jí)的線程在運(yùn)行時(shí)會(huì)具有優(yōu)先權(quán). 但這依賴于線程調(diào)度的實(shí)現(xiàn).這個(gè)實(shí)現(xiàn)是和操作系統(tǒng)相關(guān)的(OS dependent)。我們可以定義線程的優(yōu)先級(jí).但是這并不能保證高優(yōu)先級(jí)的線程會(huì)在低優(yōu)先級(jí)的線程前執(zhí)行。線程優(yōu)先級(jí)是一個(gè)int 變量(從 1-10).1 代表最低優(yōu)先級(jí).10 代表最高優(yōu)先級(jí)。

031線程類的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線程調(diào)用的?

線程類的構(gòu)造方法、靜態(tài)塊是被new這個(gè)線程類所在的線程所調(diào)用的,而run方法里面的代碼才是被線程自身所調(diào)用的。
舉個(gè)例子,假設(shè)Thread2中new了Thread1,main函數(shù)中new了Thread2,那么:
(1)Thread2的構(gòu)造方法、靜態(tài)塊是main線程調(diào)用的,Thread2的run()方法是Thread2自己調(diào)用的;
(2)Thread1的構(gòu)造方法、靜態(tài)塊是Thread2調(diào)用的,Thread1的run()方法是Thread1自己調(diào)用的;

032一個(gè)線程運(yùn)行時(shí)發(fā)生異常會(huì)怎樣?

簡(jiǎn)單的說,如果異常沒有被捕獲該線程將會(huì)停止執(zhí)行。Thread.UncaughtExceptionHandler 是用于處理未捕獲異常造成線程突然中斷情況的一個(gè)內(nèi)嵌接口。當(dāng)一個(gè)未捕獲異常將造成線程中斷的時(shí)候JVM 會(huì)使用Thread.getUncaughtExceptionHandler()來(lái)查詢線程的UncaughtExceptionHandler 并將線程和異常作為參數(shù)傳遞給handler 的uncaughtException()方法進(jìn)行處理。

033Java 線程數(shù)過多會(huì)造成什么異常?

(1)線程的生命周期開銷非常高
(2)消耗過多的CPU 資源
如果可運(yùn)行的線程數(shù)量多于可用處理器的數(shù)量,那么有線程將會(huì)被閑置。大量空閑的線程會(huì)占用許多內(nèi)存,給垃圾回收器帶來(lái)壓力,而且大量的線程在競(jìng)爭(zhēng)CPU 資源時(shí)還將產(chǎn)生其他性能的開銷。
(3)降低穩(wěn)定性
JVM 在可創(chuàng)建線程的數(shù)量上存在一個(gè)限制,這個(gè)限制值將隨著平臺(tái)的不同而不同,并且承受著多個(gè)因素制約,包括JVM 的啟動(dòng)參數(shù)、Thread 構(gòu)造函數(shù)中請(qǐng)求棧的大小,以及底層操作系統(tǒng)對(duì)線程的限制等。如果破壞了這些限制,那么可能拋出OutOfMemoryError 異常。

034多線程的常用方法?

wait();(強(qiáng)迫一個(gè)線程等待)
notify();(通知一個(gè)線程繼續(xù)執(zhí)行),
notifyAll()(所有線程繼續(xù)執(zhí)行),
sleep()(強(qiáng)迫一個(gè)線程睡眠N毫秒),
join()(等待線程終止)
yield()(線程讓步)等等;

035什么是 FutureTask?

FutureTask 表示一個(gè)異步運(yùn)算的任務(wù)。 FutureTask 里面可以傳入一個(gè) Callable 的具體實(shí)現(xiàn)類,可以對(duì)這
個(gè)異步運(yùn)算的任務(wù)的結(jié)果進(jìn)行等待獲取、判斷是否已經(jīng)完成、取消任務(wù)等操作。當(dāng)然,由于 FutureTask
也是 Runnable 接口的實(shí)現(xiàn)類,所以 FutureTask 也可以放入線程池中。

036線程之間如何進(jìn)行通訊的?

多個(gè)線程在正常情況下的運(yùn)行是互不干擾的,但是CUP對(duì)線程的切換是隨機(jī)的,這樣線程運(yùn)行的過程就脫離了我們的控制,如果我們想讓多個(gè)線程之間有規(guī)律的運(yùn)行,就需要線程通訊,線程之間通信的可以讓多個(gè)線程按照我們預(yù)期的運(yùn)行過程去執(zhí)行。
1)wait()和notify()
wait(): 當(dāng)前線程釋放鎖并且進(jìn)入等待狀態(tài)。
notify(): 喚醒當(dāng)前線程,上面wait() 的時(shí)候線程進(jìn)入了等待狀態(tài),如果我們想讓線程執(zhí)行需要通過notify()喚醒該線程。
notifyAll(): 喚醒所有進(jìn)入等待狀態(tài)的線程。
2)join()方法
join()方法的作用是使A線程加入B線程中執(zhí)行,B線程進(jìn)入阻塞狀態(tài),只有當(dāng)A線程運(yùn)行結(jié)束后B線程才會(huì)繼續(xù)執(zhí)行。
3)volatile關(guān)鍵字
volatile 關(guān)鍵字是實(shí)現(xiàn)線程變量之間真正共享的,就是我們理想中的共享狀態(tài),多個(gè)線程同時(shí)監(jiān)控著共享變量,當(dāng)變量發(fā)生變化時(shí)其它線程立即改變,具體實(shí)現(xiàn)與JMM內(nèi)存模型有關(guān)。

037線程安全的概念?

多個(gè)線程可以共享進(jìn)程的堆和方法區(qū)資源,既多個(gè)線程共享類變量。多個(gè)線程共享一個(gè)進(jìn)程的變量時(shí),如果線程對(duì)這個(gè)變量只有讀操作,沒有更新操作則這個(gè)線程沒有線程安全問題。如果線程需要對(duì)這個(gè)變量進(jìn)行修改操作,則可能會(huì)因?yàn)閿?shù)據(jù)更新不及時(shí)導(dǎo)致變量信息不準(zhǔn)確而引發(fā)線程不安全。

038線程安全如何保證?

當(dāng)多個(gè)線程對(duì)同一個(gè)資源進(jìn)行操作的時(shí)候就會(huì)有線程安全。解決線程安全的核心思想就是加鎖。加鎖有兩種方式:1.JVM提供的鎖,就是synchronized鎖,即同步代碼和同步代碼塊 2.jdk提供的各種鎖,如lock。

039Java中哪些集合是線程安全的?

在JDK1.1版本中,所有的集合都是線程安全的。但是在1.2及以后的版本中就出現(xiàn)了一些線程不安全的集合,為什么版本升級(jí)反而會(huì)出現(xiàn)一些線程不安全的集合呢?因?yàn)榫€程不安全的集合普遍比線程安全的集合效率高的多。隨著業(yè)務(wù)的發(fā)展,特別是在WEB應(yīng)用中,為了提高用戶體驗(yàn),減少用戶的等待時(shí)間,頁(yè)面的響應(yīng)速度(也就是效率)是優(yōu)先考慮的。而且對(duì)線程不安全的集合加鎖以后也能達(dá)到安全的效果(但是效率會(huì)低,因?yàn)闀?huì)有鎖的獲取以及等待)。其實(shí)在JDK源碼中相同效果的集合線程安全的比線程不安全的就多了一個(gè)同步機(jī)制,但是效率上卻低了不止一點(diǎn)點(diǎn),因?yàn)樾实停砸呀?jīng)不太建議使用了。下面列舉一些常用的線程安全的集合。
Vector:就比ArrayList多了個(gè)同步化機(jī)制。
HashTable:就比HashMap多了個(gè)線程安全。
ConcurrentHashMap:是一種高效但是線程安全的集合。
Stack:棧,線程安全,繼承與Vector。

040synchronized 關(guān)鍵字用法?

1)修飾實(shí)例方法: 作用于當(dāng)前對(duì)象實(shí)例加鎖,進(jìn)入同步代碼前要獲得 當(dāng)前對(duì)象實(shí)例的鎖

synchronized void method() {
? //業(yè)務(wù)代碼
}

2)修飾靜態(tài)方法: 也就是給當(dāng)前類加鎖,會(huì)作用于類的所有對(duì)象實(shí)例 ,進(jìn)入同步代碼前要獲得 當(dāng)前 class 的鎖。因?yàn)殪o態(tài)成員不屬于任何一個(gè)實(shí)例對(duì)象,是類成員( static 表明這是該類的一個(gè)靜態(tài)資源,不管 new 了多少個(gè)對(duì)象,只有一份)。所以,如果一個(gè)線程 A 調(diào)用一個(gè)實(shí)例對(duì)象的非靜態(tài) synchronized 方法,而線程 B 需要調(diào)用這個(gè)實(shí)例對(duì)象所屬類的靜態(tài) synchronized 方法,是允許的,不會(huì)發(fā)生互斥現(xiàn)象,因?yàn)樵L問靜態(tài) synchronized 方法占用的鎖是當(dāng)前類的鎖,而訪問非靜態(tài) synchronized 方法占用的鎖是當(dāng)前實(shí)例對(duì)象鎖。

synchronized void staic method() {
? //業(yè)務(wù)代碼
}

3)修飾代碼塊 :指定加鎖對(duì)象,對(duì)給定對(duì)象/類加鎖。synchronized(this|object) 表示進(jìn)入同步代碼庫(kù)前要獲得給定對(duì)象的鎖。synchronized(類.class) 表示進(jìn)入同步代碼前要獲得 當(dāng)前 class 的鎖

synchronized(this) {
? //業(yè)務(wù)代碼
}

總結(jié):synchronized 關(guān)鍵字加到 static 靜態(tài)方法和 synchronized(class) 代碼塊上都是是給 Class 類上鎖。
synchronized 關(guān)鍵字加到實(shí)例方法上是給對(duì)象實(shí)例上鎖。
盡量不要使用 synchronized(String a) 因?yàn)?JVM 中,字符串常量池具有緩存功能!

041構(gòu)造方法可以使用 synchronized 關(guān)鍵字修飾么?

構(gòu)造方法不能使用 synchronized 關(guān)鍵字修飾。構(gòu)造方法本身就屬于線程安全的,不存在同步的構(gòu)造方法一說。

042synchronized使用:雙重校驗(yàn)鎖實(shí)現(xiàn)對(duì)象單例(線程安全)。

public class Singleton {
    private static Singleton uniqueInstance;
	// 私有化構(gòu)造方法
    private Singleton() {
    }
	// 提供getInstance方法
    public  static Singleton getInstance() {
       //先判斷對(duì)象是否已經(jīng)實(shí)例過,沒有實(shí)例化過才進(jìn)入加鎖代碼
        if (uniqueInstance == null) {
            //類對(duì)象加鎖
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

043同步和異步的區(qū)別?

同步:功能調(diào)用時(shí),在沒有得到結(jié)果之前,該調(diào)用就不返回或繼續(xù)執(zhí)行后續(xù)操作。這時(shí)程序是阻塞的,只有接收到返回的值或消息后才往下執(zhí)行其他的命令。因此,簡(jiǎn)單來(lái)說,同步就是必須一件一件做事,等前一件事做完了才能做完下一件事。
異步:與同步相對(duì),當(dāng)一個(gè)異步過程調(diào)用發(fā)出后,調(diào)用者在沒有得到結(jié)果之前,就可以繼續(xù)執(zhí)行后續(xù)操作,當(dāng)這個(gè)調(diào)用完成后,一般通過狀態(tài)或者回調(diào)來(lái)通知調(diào)用者。

044同步方法和同步塊,哪個(gè)是更好的選擇?

同步塊,這意味著同步塊之外的代碼是異步執(zhí)行的,這比同步整個(gè)方法更提升代 碼的效率。請(qǐng)知道一條原則:同步的范圍越小越好。

045為什么 wait(), notify()和 notifyAll()必須在同步方法或者同步塊中被調(diào)用?

當(dāng)一個(gè)線程需要調(diào)用對(duì)象的wait()方法的時(shí)候,這個(gè)線程必須擁有該對(duì)象的鎖,接著它就會(huì)釋放這個(gè)對(duì)象鎖并進(jìn)入等待狀態(tài)直到其他線程調(diào)用這個(gè)對(duì)象上的notify()方法。同樣的,當(dāng)一個(gè)線程需要調(diào)用對(duì)象的notify()方法時(shí),它會(huì)釋放這個(gè)對(duì)象的鎖,以便其他在等待的線程就可以得到這個(gè)對(duì)象鎖。由于所有的這些方法都需要線程持有對(duì)象的鎖,這樣就只能通過同步來(lái)實(shí)現(xiàn),所以他們只能在同步方法或者同步塊中被調(diào)用。

046什么是線程同步和線程互斥?

同步就是協(xié)同步調(diào),按預(yù)定的先后次序進(jìn)行運(yùn)行。如:你說完,我再說。這里的同步千萬(wàn)不要理解成那個(gè)同時(shí)進(jìn)行,應(yīng)是指協(xié)同、協(xié)助、互相配合。線程同步是指多線程通過特定的設(shè)置(如互斥量,事件對(duì)象,臨界區(qū))來(lái)控制線程之間的執(zhí)行順序(即所謂的同步)也可以說是在線程之間通過同步建立起執(zhí)行順序的關(guān)系,如果沒有同步,那線程之間是各自運(yùn)行各自的!
線程互斥是指對(duì)于共享的進(jìn)程系統(tǒng)資源,在各單個(gè)線程訪問時(shí)的排它性。當(dāng)有若干個(gè)線程都要使用某一共享資源時(shí),任何時(shí)刻最多只允許一個(gè)線程去使用,其它要使用該資源的線程必須等待,直到占用資源者釋放該資源。線程互斥可以看成是一種特殊的線程同步(下文統(tǒng)稱為同步)。

047什么是線程池?

線程池就是提前創(chuàng)建若干個(gè)線程,如果有任務(wù)需要處理,線程池里的線程就會(huì)處理任務(wù),處理完之后線程并不會(huì)被銷毀,而是等待下一個(gè)任務(wù)。由于創(chuàng)建和銷毀線程都是消耗系統(tǒng)資源的,所以當(dāng)你想要頻繁的創(chuàng)建和銷毀線程的時(shí)候就可以考慮使用線程池來(lái)提升系統(tǒng)的性能。

048為什么要用線程池?

java中經(jīng)常需要用到多線程來(lái)處理一些業(yè)務(wù),我們非常不建議單純使用繼承Thread或者實(shí)現(xiàn)Runnable接口的方式來(lái)創(chuàng)建線程,那樣勢(shì)必有創(chuàng)建及銷毀線程耗費(fèi)資源、線程上下文切換問題。同時(shí)創(chuàng)建過多的線程也可能引發(fā)資源耗盡的風(fēng)險(xiǎn),這個(gè)時(shí)候引入線程池比較合理,方便線程任務(wù)的管理。java中涉及到線程池的相關(guān)類均在jdk1.5開始的java.util.concurrent包中,涉及到的幾個(gè)核心類及接口包括:Executor、Executors、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等。

049線程池的優(yōu)勢(shì)?

線程池提供了一種限制和管理資源(包括執(zhí)行一個(gè)任務(wù))。 每個(gè)線程池還維護(hù)一些基本統(tǒng)計(jì)信息,例如已完成任務(wù)的數(shù)量。
線程池的好處如下:
1.降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
2.可有效的控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過多資源競(jìng)爭(zhēng),避免堵塞。
3.提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。

050線程池的工作原理?

1)線程提交到線程池
2)判斷核心線程池是否已經(jīng)達(dá)到設(shè)定的數(shù)量,如果沒有達(dá)到,則直接創(chuàng)建線程執(zhí)行任務(wù)
3)如果達(dá)到了,則放在隊(duì)列中,等待執(zhí)行
4)如果隊(duì)列已經(jīng)滿了,則判斷線程的數(shù)量是否已經(jīng)達(dá)到設(shè)定的最大值,如果達(dá)到了,則直接執(zhí)行拒絕策略
5)如果沒有達(dá)到,則創(chuàng)建線程執(zhí)行任務(wù)。

051線程池都有哪些狀態(tài)?

RUNNING :能接受新提交的任務(wù),并且也能處理阻塞隊(duì)列中的任務(wù);
SHUTDOWN:關(guān)閉狀態(tài),不再接受新提交的任務(wù),但卻可以繼續(xù)處理阻塞隊(duì)列中已保存的任務(wù)。在線程池處于 RUNNING 狀態(tài)時(shí),調(diào)用 shutdown()方法會(huì)使線程池進(jìn)入到該狀態(tài)。(finalize() 方法在執(zhí)行過程中也會(huì)調(diào)用shutdown()方法進(jìn)入該狀態(tài));
STOP:不能接受新任務(wù),也不處理隊(duì)列中的任務(wù),會(huì)中斷正在處理任務(wù)的線程。在線程池處于 RUNNING 或 SHUTDOWN 狀態(tài)時(shí),調(diào)用 shutdownNow() 方法會(huì)使線程池進(jìn)入到該狀態(tài);
TIDYING:如果所有的任務(wù)都已終止了,workerCount (有效線程數(shù)) 為0,線程池進(jìn)入該狀態(tài)后會(huì)調(diào)用 terminated() 方法進(jìn)入TERMINATED 狀態(tài)。
TERMINATED:在terminated() 方法執(zhí)行完后進(jìn)入該狀態(tài),默認(rèn)terminated()方法中什么也沒有做。

052什么是Executors?

Executor就是一個(gè)線程池框架,Executor 位于java.util.concurrent.Executors ,提供了用于創(chuàng)建工作線程的線程池的工廠方法。它包含一組用于有效管理工作線程的組件。Executor API 通過 Executors 將任務(wù)的執(zhí)行與要執(zhí)行的實(shí)際任務(wù)解耦。

053在 Java 中 Executor 和 Executors 的區(qū)別?

Executor 接口對(duì)象能執(zhí)行我們的線程任務(wù);
Executors 工具類的不同方法按照我們的需求創(chuàng)建了不同的線程池,來(lái)滿足業(yè)務(wù)的需求。
ExecutorService 接口繼承了Executor接口并進(jìn)行了擴(kuò)展,提供了更多的方法,我們能夠獲得任務(wù)執(zhí)行的狀態(tài)并且可以獲取任務(wù)的返回值。

054Executors新建線程池類型?

1.newSingleThreadExecutor
創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。
2.newFixedThreadPoo
創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。
3.newCachedThreadPool
創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程。
4.newScheduledThreadPool
創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。

055Executors新建線程池的弊端?

1)newFixedThreadPool和newSingleThreadExecutor: 主要問題是堆積的請(qǐng)求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool: 主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE,可能會(huì)創(chuàng)建數(shù)量非常多的線程,甚至OOM。
3)線程池不允許使用Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓程序員更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。

056什么是ThreadPoolExecutor?

ThreadPoolExecutor是線程池的核心實(shí)現(xiàn)類,在JDK1.5引入,位于java.util.concurrent包。

057通過ThreadPoolExecutor如何創(chuàng)建線程池?

通過下面的demo來(lái)了解ThreadPoolExecutor創(chuàng)建線程的過程。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
/**
 * 測(cè)試ThreadPoolExecutor對(duì)線程的執(zhí)行順序
 **/
public class ThreadPoolSerialTest {
    public static void main(String[] args) {
        //核心線程數(shù)
        int corePoolSize = 3;
        //最大線程數(shù)
        int maximumPoolSize = 6;
        //超過 corePoolSize 線程數(shù)量的線程最大空閑時(shí)間
        long keepAliveTime = 2;
        //以秒為時(shí)間單位
        TimeUnit unit = TimeUnit.SECONDS;
        //創(chuàng)建工作隊(duì)列,用于存放提交的等待執(zhí)行任務(wù)
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(2);
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            //創(chuàng)建線程池
            threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
                    maximumPoolSize,
                    keepAliveTime,
                    unit,
                    workQueue,
                    new ThreadPoolExecutor.AbortPolicy());
 
            //循環(huán)提交任務(wù)
            for (int i = 0; i < 8; i++) {
                //提交任務(wù)的索引
                final int index = (i + 1);
                threadPoolExecutor.submit(() -> {
                    //線程打印輸出
                    System.out.println("大家好,我是線程:" + index);
                    try {
                        //模擬線程執(zhí)行時(shí)間,10s
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                //每個(gè)任務(wù)提交后休眠500ms再提交下一個(gè)任務(wù),用于保證提交順序
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();
        }
    }
}

當(dāng)一個(gè)新任務(wù)被提交時(shí):
1. 當(dāng)前活躍線程數(shù)<corePoolSize,則創(chuàng)建一個(gè)新線程執(zhí)行新任務(wù);
2. 當(dāng)前活躍線程數(shù)>corePoolSize,且隊(duì)列(workQueue)未滿時(shí),則將新任務(wù)放入隊(duì)列中;
3. 當(dāng)前活躍線程數(shù)>corePoolSize,且隊(duì)列(workQueue)已滿,且當(dāng)前活躍線程數(shù)<maximumPoolSize,則繼續(xù)創(chuàng)建一個(gè)新線程執(zhí)行新任務(wù);
4. 當(dāng)前活躍線程數(shù)>corePoolSize,且隊(duì)列(workQueue)已滿,且當(dāng)前活躍線程數(shù)=maximumPoolSize,則執(zhí)行拒絕策略(handler);
當(dāng)任務(wù)執(zhí)行完成后:
1. 超出corePoolSize的空閑線程,在等待新任務(wù)時(shí),如果超出了keepAliveTime,則線程會(huì)被銷毀;
2. 如果allowCoreThreadTimeOut被設(shè)置為true,那么corePoolSize以內(nèi)的空閑線程,如果超出了keepAliveTime,則同樣會(huì)被銷毀。

058ThreadPoolExecutor線程池中的幾種重要的參數(shù)?

corePoolSize就是線程池中的核心線程數(shù)量,這幾個(gè)核心線程在沒有用的時(shí)候,也不會(huì)被回收
maximumPoolSize就是線程池中可以容納的最大線程的數(shù)量
keepAliveTime就是線程池中除了核心線程之外的其他的最長(zhǎng)可以保留的時(shí)間,因?yàn)樵诰€程池中,除了核心線程即使在無(wú)任務(wù)的情況下也不能被清 除,其余的都是有存活時(shí)間的,意思就是非核心線程可以保留的最長(zhǎng)的空閑時(shí)間
util就是計(jì)算這個(gè)時(shí)間的一個(gè)單位。
workQueue就是等待隊(duì)列,任務(wù)可以儲(chǔ)存在任務(wù)隊(duì)列中等待被執(zhí)行,執(zhí)行的是FIFIO原則(先進(jìn)先出)。
threadFactory就是創(chuàng)建線程的線程工廠。
handler是一種拒絕策略,我們可以在任務(wù)滿了之后,拒絕執(zhí)行某些任務(wù)。

059ThreadPoolExecutor飽和(拒絕)策略是什么?

當(dāng)線程充滿了ThreadPool的有界隊(duì)列時(shí),飽和策略開始起作用。飽和策略可以理解為隊(duì)列飽和后,處理后續(xù)無(wú)法入隊(duì)的任務(wù)的策略。ThreadPoolExecutor可以通過調(diào)用setRejectedExecutionHandler來(lái)修改飽和策略。

060說說線程池的拒絕策略?

當(dāng)請(qǐng)求任務(wù)不斷的過來(lái),而系統(tǒng)此時(shí)又處理不過來(lái)的時(shí)候,我們需要采取的策略是拒絕服務(wù)。RejectedExecutionHandler接口提供了拒絕任務(wù)處理的自定義方法的機(jī)會(huì)。在ThreadPoolExecutor中已經(jīng)包含四種處理策略。
AbortPolicy策略:該策略會(huì)直接拋出異常,阻止系統(tǒng)正常工作。
CallerRunsPolicy 策略:只要線程池未關(guān)閉,該策略直接在調(diào)用者線程中,運(yùn)行當(dāng)前的被丟棄的任務(wù)。
DiscardOleddestPolicy策略:該策略將丟棄最老的一個(gè)請(qǐng)求,也就是即將被執(zhí)行的任務(wù),并嘗試再次提交當(dāng)前任務(wù)。
DiscardPolicy策略:該策略默默的丟棄無(wú)法處理的任務(wù),不予任何處理。
除了JDK默認(rèn)提供的四種拒絕策略,我們可以根據(jù)自己的業(yè)務(wù)需求去自定義拒絕策略,自定義的方式很簡(jiǎn)單,直接實(shí)現(xiàn)RejectedExecutionHandler接口即可。

061 ThreadPoolExecutor的execute()方法和 submit()方法的區(qū)別是什么呢?

1.execute()方法用于提交不需要返回值的任務(wù),所以無(wú)法判斷任務(wù)是否被線程池執(zhí)行成功與否;
2.submit()方法用于提交需要返回值的任務(wù)。線程池會(huì)返回一個(gè) Future 類型的對(duì)象,通過這個(gè) Future 對(duì)象可以判斷任務(wù)是否執(zhí)行成功,并且可以通過 Future 的 get()方法來(lái)獲取返回值,get()方法會(huì)阻塞當(dāng)前線程直到任務(wù)完成,而使用 get(long timeout,TimeUnit unit)方法則會(huì)阻塞當(dāng)前線程一段時(shí)間后立即返回,這時(shí)候有可能任務(wù)沒有執(zhí)行完。

062什么是線程組,為什么在 Java 中不推薦使用?

線程組ThreadGroup對(duì)象中的stop,resume,suspend會(huì)導(dǎo)致安全問題,主要是死鎖問題,已經(jīng)被官方廢棄,多以價(jià)值已經(jīng)大不如以前。
線程組ThreadGroup不是線程安全的,在使用過程中不能及時(shí)獲取安全的信息。

063線程池如何關(guān)閉?

shutdownNow():立即關(guān)閉線程池(暴力),正在執(zhí)行中的及隊(duì)列中的任務(wù)會(huì)被中斷,同時(shí)該方法會(huì)返回被中斷的隊(duì)列中的任務(wù)列表;
shutdown():平滑關(guān)閉線程池,正在執(zhí)行中的及隊(duì)列中的任務(wù)能執(zhí)行完成,后續(xù)進(jìn)來(lái)的任務(wù)會(huì)被執(zhí)行拒絕策略;
isTerminated():當(dāng)正在執(zhí)行的任務(wù)及對(duì)列中的任務(wù)全部都執(zhí)行(清空)完就會(huì)返回true;

064線程池的復(fù)用原理?

線程池將線程和任務(wù)進(jìn)行解耦,線程是線程,任務(wù)是任務(wù),擺脫了之前通過 Thread 創(chuàng)建線程時(shí)的一個(gè)線程必須對(duì)應(yīng)一個(gè)任務(wù)的限制。在線程池中,同一個(gè)線程可以從阻塞隊(duì)列中不斷獲取新任務(wù)來(lái)執(zhí)行,其核心原理在于線程池對(duì) Thread 進(jìn)行了封裝,并不是每次執(zhí)行任務(wù)都會(huì)調(diào)用 Thread.start() 來(lái)創(chuàng)建新線程,而是讓每個(gè)線程去執(zhí)行一個(gè)“循環(huán)任務(wù)”,在這個(gè)“循環(huán)任務(wù)”中不停的檢查是否有任務(wù)需要被執(zhí)行,如果有則直接執(zhí)行,也就是調(diào)用任務(wù)中的 run 方法,將 run 方法當(dāng)成一個(gè)普通的方法執(zhí)行,通過這種方式將只使用固定的線程就將所有任務(wù)的 run 方法串聯(lián)起來(lái)。

065線程池都有哪幾種工作隊(duì)列?

1.ArrayBlockingQueue是一個(gè)基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列,此隊(duì)列按 FIFO(先進(jìn)先出)原則對(duì)元素進(jìn)行排序。
2.LinkedBlockingQueue一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,此隊(duì)列按FIFO (先進(jìn)先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個(gè)隊(duì)列
3.SynchronousQueue 一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工廠方法Executors.newCachedThreadPool()使用了這個(gè)隊(duì)列。
4.PriorityBlockingQueue 一個(gè)具有優(yōu)先級(jí)的無(wú)限阻塞隊(duì)列。

066SpringBoot如何整合線程池?

首先是利用好SpringBoot的自動(dòng)裝配功能,配置好線程池的一些基本參數(shù)。

@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {

    /*
    * 線程池名前綴
    */
    private static final String threadNamePrefix = "Api-Async-";


    /**
     * bean的名稱, 默認(rèn)為首字母小寫的方法名
     * @return
     */
    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        /**
     * 默認(rèn)情況下,在創(chuàng)建了線程池后,線程池中的線程數(shù)為0,當(dāng)有任務(wù)來(lái)之后,就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行任務(wù),
     * 當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后,就會(huì)把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中;
     * 當(dāng)隊(duì)列滿了,就繼續(xù)創(chuàng)建線程,當(dāng)線程數(shù)量大于等于maxPoolSize后,開始使用拒絕策略拒絕
     */
    /*
    * 核心線程數(shù)(默認(rèn)線程數(shù))
    */
        executor.setCorePoolSize(corePoolSize);
        //最大線程數(shù)
        executor.setMaxPoolSize(maxPoolSize);
        //緩沖隊(duì)列數(shù)
        executor.setQueueCapacity(queueCapacity);
        //允許線程空閑時(shí)間(單位是秒)
        executor.setKeepAliveSeconds(keepAliveTime);
        executor.setThreadNamePrefix(threadNamePrefix);
        //用來(lái)設(shè)置線程池關(guān)閉時(shí)候等待所有任務(wù)都完成再繼續(xù)銷毀其他的Bean
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //線程池對(duì)拒絕任務(wù)的處理策略,CallerRunsPolicy:由調(diào)用線程(提交任務(wù)的線程)處理該任務(wù)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //初始化
        executor.initialize();
        return executor;
    }
}

配置好線程池的基本參數(shù)時(shí)候,我們就可以使用線程池了, 只要在一個(gè)限定域?yàn)閜ublic的方法頭部加上@Async注解即可。

@Async
public void createOrder() {
    System.out.println("執(zhí)行任務(wù)"); 
}

067線程池的優(yōu)化?

1)分析任務(wù)的特性
任務(wù)的性質(zhì):CPU 密集型任務(wù)、IO 密集型任務(wù)和混合型任務(wù)。
任務(wù)的優(yōu)先級(jí):高、中、低。
任務(wù)的執(zhí)行時(shí)間:長(zhǎng)、中、短。
任務(wù)的依賴性:是否依賴其他系統(tǒng)資源,如數(shù)據(jù)庫(kù)連接。
2)具體策略
[1]CPU 密集型任務(wù)配置盡可能小的線程,如配置N(CPU核心數(shù))+1個(gè)線程的線程池。
[2]IO 密集型任務(wù)則由于線程并不是一直在執(zhí)行任務(wù),則配置盡可能多的線程,如2*N(CPU核心數(shù))。
[3]混合型任務(wù)如果可以拆分,則將其拆分成一個(gè) CPU 密集型任務(wù)和一個(gè) IO 密集型任務(wù)。只要這兩個(gè)任務(wù)執(zhí)行的時(shí)間相差不是太大,那么分解后執(zhí)行的吞吐率要高于串行執(zhí)行的吞吐率;如果這兩個(gè)任務(wù)執(zhí)行時(shí)間相差太大,則沒必要進(jìn)行分解。
[4]優(yōu)先級(jí)不同的任務(wù)可以使用優(yōu)先級(jí)隊(duì)列 PriorityBlockingQueue 來(lái)處理,它可以讓優(yōu)先級(jí)高的任務(wù)先得到執(zhí)行。但是,如果一直有高優(yōu)先級(jí)的任務(wù)加入到阻塞隊(duì)列中,那么低優(yōu)先級(jí)的任務(wù)可能永遠(yuǎn)不能執(zhí)行。
[5]執(zhí)行時(shí)間不同的任務(wù)可以交給不同規(guī)模的線程池來(lái)處理,或者也可以使用優(yōu)先級(jí)隊(duì)列,讓執(zhí)行時(shí)間短的任務(wù)先執(zhí)行。
[6]依賴數(shù)據(jù)庫(kù)連接池的任務(wù),因?yàn)榫€程提交 SQL 后需要等待數(shù)據(jù)庫(kù)返回結(jié)果,線程數(shù)應(yīng)該設(shè)置得較大,這樣才能更好的利用 CPU。
[7]建議使用有界隊(duì)列,有界隊(duì)列能增加系統(tǒng)的穩(wěn)定性和預(yù)警能力。可以根據(jù)需要設(shè)大一點(diǎn),比如幾千。使用無(wú)界隊(duì)列,線程池的隊(duì)列就會(huì)越來(lái)越大,有可能會(huì)撐滿內(nèi)存,導(dǎo)致整個(gè)系統(tǒng)不可用。

068什么是ThreadLocal?

從字面上理解ThreadLocal就是“線程局部變量”的意思。簡(jiǎn)單的說就是,一個(gè)ThreadLocal在一個(gè)線程中是共享的,在不同線程之間又是隔離的(每個(gè)線程都只能看到自己線程的值)。
學(xué)習(xí)一個(gè)類之前我們需要了解一個(gè)類的API,這也是我們學(xué)習(xí)類的入口。而ThreadLocal類的API相當(dāng)簡(jiǎn)單。
在這里面比較重要的就是,get、set、remove了,這三個(gè)方法是對(duì)這個(gè)變量進(jìn)行操作的關(guān)鍵。set用于賦值操作,get用于獲取變量中的值,remove就是刪除當(dāng)前這個(gè)變量的值。需要注意的是initialValue方法會(huì)在第一次調(diào)用時(shí)被觸發(fā),用于初始化當(dāng)前變量值,例如在下列代碼中我們需要?jiǎng)?chuàng)建一個(gè)ThreadLocal,用于創(chuàng)建一個(gè)與線程綁定的Connection對(duì)象:

ThreadLocal connection = new ThreadLocal(){
? ? public Connection initialValue(){
? ? ? ? return DriverManager.getConnection(…);
? ? }
});

為什么我們將ThreadLocal說成變量,我們姑且可以這么理解,每個(gè)ThreadLocal實(shí)例中都可以保存一個(gè)值(基本數(shù)據(jù)類型值或者引用類型的引用值),而內(nèi)部保存的值是可以修改的,而這樣的特性與變量的特性及其相似,變量不就是用來(lái)保存一個(gè)值的嗎?也就是說每一個(gè)ThreadLocal實(shí)例就類似于一個(gè)變量名,不同的ThreadLocal實(shí)例就是不同的變量名,它們內(nèi)部會(huì)存有一個(gè)值(暫時(shí)這么理解)在后面的描述中所說的“ThreadLocal變量或者是線程變量”代表的就是ThreadLocal類的實(shí)例。我們通過重寫initialValue方法指定ThreadLocal變量的初始值,默認(rèn)情況下initialValue返回的是null。
 

069ThreadLocal的具體實(shí)現(xiàn)?

接下來(lái)我們就來(lái)動(dòng)手實(shí)踐一下,來(lái)理解前面沒有理解的那句話:一個(gè)ThreadLocal在一個(gè)線程中是共享的,在不同線程之間又是隔離的(每個(gè)線程都只能看到自己線程的值)

public class ThreadLocalTest {
?? ?private static ThreadLocal<Integer> num = new ThreadLocal<Integer>() {
?? ??? ?// 重寫這個(gè)方法,可以修改“線程變量”的初始值,默認(rèn)是null
?? ??? ?@Override
?? ??? ?protected Integer initialValue() {
?? ??? ??? ?return 0;
?? ??? ?}
?? ?};
?
?? ?public static void main(String[] args) {
?? ??? ?// 創(chuàng)建一號(hào)線程
?? ??? ?new Thread(new Runnable() {
?? ??? ??? ?@Override
?? ??? ??? ?public void run() {
?? ??? ??? ??? ?// 在一號(hào)線程中將ThreadLocal變量設(shè)置為1
?? ??? ??? ??? ?num.set(1);
?? ??? ??? ??? ?System.out.println("一號(hào)線程中ThreadLocal變量中保存的值為:" + num.get());
?? ??? ??? ?}
?? ??? ?}).start();
?
?? ??? ?// 創(chuàng)建二號(hào)線程
?? ??? ?new Thread(new Runnable() {
?? ??? ??? ?@Override
?? ??? ??? ?public void run() {
?? ??? ??? ??? ?num.set(2);
?? ??? ??? ??? ?System.out.println("二號(hào)線程中ThreadLocal變量中保存的值為:" + num.get());
?? ??? ??? ?}
?? ??? ?}).start();
?
?? ??? ?//為了讓一二號(hào)線程執(zhí)行完畢,讓主線程睡500ms
?? ??? ?try {
?? ??? ??? ?Thread.sleep(500);
?? ??? ?} catch (InterruptedException e) {
?? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ?e.printStackTrace();
?? ??? ?}
?? ??? ?
?? ??? ?System.out.println("主線程中ThreadLocal變量中保存的值:" + num.get());
?? ?}
}

上面的代碼中在類中創(chuàng)建了一個(gè)靜態(tài)的“ThreadLocal變量”,在主線程中創(chuàng)建兩個(gè)線程,在這兩個(gè)線程中分別設(shè)置ThreadLocal變量為1和2。然后等待一號(hào)和二號(hào)線程執(zhí)行完畢后,在主線程中查看ThreadLocal變量的值。
程序結(jié)果及分析:
程序結(jié)果重點(diǎn)看的是主線程輸出的是0,如果是一個(gè)普通變量,在一號(hào)線程和二號(hào)線程中將普通變量設(shè)置為1和2,那么在一二號(hào)線程執(zhí)行完畢后在打印這個(gè)變量,輸出的值肯定是1或者2(到底輸出哪一個(gè)由操作系統(tǒng)的線程調(diào)度邏輯有關(guān))。但使用ThreadLocal變量通過兩個(gè)線程賦值后,在主線程程中輸出的卻是初始值0。在這也就是為什么“一個(gè)ThreadLocal在一個(gè)線程中是共享的,在不同線程之間又是隔離的”,每個(gè)線程都只能看到自己線程的值,這也就是ThreadLocal的核心作用:實(shí)現(xiàn)線程范圍的局部變量。
 

070ThreadLocal的原理分析?

我們還是將最后結(jié)論擺在前面,每個(gè)Thread對(duì)象都有一個(gè)ThreadLocalMap,當(dāng)創(chuàng)建一個(gè)ThreadLocal的時(shí)候,就會(huì)將該ThreadLocal對(duì)象添加到該Map中,其中鍵就是ThreadLocal,值可以是任意類型。也就是說,想要存入的ThreadLocal中的數(shù)據(jù)實(shí)際上并沒有存到ThreadLocal對(duì)象中去,而是以這個(gè)ThreadLocal實(shí)例作為key存到了當(dāng)前線程中的一個(gè)Map中去了,獲取ThreadLocal的值時(shí)同樣也是這個(gè)道理。這也就是為什么ThreadLocal可以實(shí)現(xiàn)線程之間隔離的原因了。
總結(jié):
1)ThreadLocal的作用:實(shí)現(xiàn)線程范圍內(nèi)的局部變量,即ThreadLocal在一個(gè)線程中是共享的,在不同線程之間是隔離的。
2)ThreadLocal的原理:ThreadLocal存入值時(shí)使用當(dāng)前ThreadLocal實(shí)例作為key,存入當(dāng)前線程對(duì)象中的Map中去。最開始在看源碼之前,我以為是以當(dāng)前線程對(duì)象作為key將對(duì)象存入到ThreadLocal中的Map中去。

071ThreadLocal和Synchonized區(qū)別?

兩者都用于解決多線程并發(fā)訪問。但是ThreadLocal與synchronized有本質(zhì)的區(qū)別。Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。Synchronized是利用鎖的機(jī)制,使變量或代碼塊在某一時(shí)該只能被一個(gè)線程訪問。而ThreadLocal為每一個(gè)線程都提供了變量的副本,使得每個(gè)線程在某一時(shí)間訪問到的并不是同一個(gè)對(duì)象,這樣就隔離了多個(gè)線程對(duì)數(shù)據(jù)的數(shù)據(jù)共享。而Synchronized卻正好相反,它用于在多個(gè)線程間通信時(shí)能夠獲得數(shù)據(jù)共享。

072ThreadLocal內(nèi)存泄漏以及解決方案?

如果ThreadLocal沒有外部強(qiáng)引用,那么在發(fā)生垃圾回收的時(shí)候,ThreadLocal就必定會(huì)被回收,而ThreadLocal又作為Map中的key,ThreadLocal被回收就會(huì)導(dǎo)致一個(gè)key為null的entry,外部就無(wú)法通過key來(lái)訪問這個(gè)entry,垃圾回收也無(wú)法回收,這就造成了內(nèi)存泄漏
解決方案:每次使用完ThreadLocal都調(diào)用它的remove()方法清除數(shù)據(jù),或者按照J(rèn)DK建議將ThreadLocal變量定義成private static,這樣就一直存在ThreadLocal的強(qiáng)引用,也就能保證任何時(shí)候都能通過ThreadLocal的弱引用訪問到Entry的value值,進(jìn)而清除掉。

目錄

001為什么要使用多線程呢? 002多線程應(yīng)用場(chǎng)景? 003多線程的好處? 004使用多線程可能帶來(lái)什么問題? 005線程和進(jìn)程的區(qū)別? 006創(chuàng)建線程的幾種方式? 007創(chuàng)建線程用Runnable還是Thread? 008實(shí)現(xiàn)Runnable接口和Callable接口的區(qū)別? 009線程的 run()和 start()有什么區(qū)別? 010說說線程的生命周期和狀態(tài)? 011什么是上下文切換? 012如何創(chuàng)建守護(hù)線程? 013用戶線程和守護(hù)線程有什么區(qū)別? 014為什么Thread類的sleep()和yield()方法是靜態(tài)的? 015如何在 Windows 和 Linux 上查找哪個(gè)線程cpu利用率最高? 016為什么我們調(diào)用 start() 方法時(shí)會(huì)執(zhí)行 run() 方法,為什么我們不能直接調(diào)用 run() 方法? 017什么是 Callable 和 Future? 018線程調(diào)度策略有哪些? 019Java中用到的線程調(diào)度算法是什么? 020什么是線程調(diào)度器(Thread Scheduler)和時(shí)間分片(Time Slicing )? 021說說 sleep() 方法和 wait() 方法區(qū)別和共同點(diǎn)? 022為什么線程通信的方法 wait(), notify()和 notifyAll()被定義在 Object 類里? 023Thread 類中的 yield 方法有什么作用? 024為什么 Thread 類的 sleep()和 yield ()方法是靜態(tài)的? 025線程的 sleep()方法和 yield()方法有什么區(qū)別? 026多線程的中斷是什么? 027如何停止一個(gè)正在運(yùn)行的線程? 028Java 中 interrupted 和 isInterrupted 方法的區(qū)別? 029notify() 和 notifyAll() 有什么區(qū)別? 030你對(duì)線程優(yōu)先級(jí)的理解是什么? 031線程類的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線程調(diào)用的? 032一個(gè)線程運(yùn)行時(shí)發(fā)生異常會(huì)怎樣? 033Java 線程數(shù)過多會(huì)造成什么異常? 034多線程的常用方法? 035什么是 FutureTask? 036線程之間如何進(jìn)行通訊的? 037線程安全的概念? 038線程安全如何保證? 039Java中哪些集合是線程安全的? 040synchronized 關(guān)鍵字用法? 041構(gòu)造方法可以使用 synchronized 關(guān)鍵字修飾么? 042synchronized使用:雙重校驗(yàn)鎖實(shí)現(xiàn)對(duì)象單例(線程安全)。 043同步和異步的區(qū)別? 044同步方法和同步塊,哪個(gè)是更好的選擇? 045為什么 wait(), notify()和 notifyAll()必須在同步方法或者同步塊中被調(diào)用? 046什么是線程同步和線程互斥? 047什么是線程池? 048為什么要用線程池? 049線程池的優(yōu)勢(shì)? 050線程池的工作原理? 051線程池都有哪些狀態(tài)? 052什么是Executors? 053在 Java 中 Executor 和 Executors 的區(qū)別? 054Executors新建線程池類型? 055Executors新建線程池的弊端? 056什么是ThreadPoolExecutor? 057通過ThreadPoolExecutor如何創(chuàng)建線程池? 058ThreadPoolExecutor線程池中的幾種重要的參數(shù)? 059ThreadPoolExecutor飽和(拒絕)策略是什么? 060說說線程池的拒絕策略? 061 ThreadPoolExecutor的execute()方法和 submit()方法的區(qū)別是什么呢? 062什么是線程組,為什么在 Java 中不推薦使用? 063線程池如何關(guān)閉? 064線程池的復(fù)用原理? 065線程池都有哪幾種工作隊(duì)列? 066SpringBoot如何整合線程池? 067線程池的優(yōu)化? 068什么是ThreadLocal? 069ThreadLocal的具體實(shí)現(xiàn)? 070ThreadLocal的原理分析? 071ThreadLocal和Synchonized區(qū)別? 072ThreadLocal內(nèi)存泄漏以及解決方案?
返回頂部
主站蜘蛛池模板: 久久91综合国产91久久精品 | 四虎影视免费观看 | 最新欧美精品一区二区三区不卡 | 久久国产这里只精品免费 | 一级特级欧美aa毛片免费 | 亚洲热在线 | 免费国产成人高清在线观看视频 | 九九精品影院 | 日韩精品一区二区三区免费视频 | 精品久久天干天天天按摩 | 日韩女同视频 | 精品久久伊人 | 黄色生活毛片 | 欧美日韩国产58香蕉在线视频 | 香蕉人精品视频多人免费永久视频 | 国产精品欧美韩国日本久久 | 寡妇野外啪啪一区二区 | 国产精品久久久久久久hd | 999久久久精品视频在线观看 | 在线精品国内视频秒播 | 997在线观看视频国产 | 免看一级a毛片一片成人不卡 | 97国内免费久久久久久久久久 | 亚洲欧美国产另类 | 最好看的毛片 | 青青青在线视频国产 | 啪啪短视频 | 97国产在线公开免费观看 | 离线枕边人国语在线影视 | 亚洲一区二区三区视频 | 欧美精欧美乱码一二三四区 | 亚洲欧美网站 | 天天操天天插天天干 | 免费视频爱爱 | 热99re久久精品2久久久 | 波多野结衣一二三区 | 欧美性在线播放 | 国产99久久久久久免费看 | 国产精品免费入口视频 | 曰本不卡视频 | 羞羞视频免费观看网站 |