1. 使用多線程有什么好處?
多線程有多種好處仗扬,如下所示:
- 即使程序的一部分被阻塞症概,也允許程序繼續(xù)運(yùn)行。
- 與使用多個(gè)進(jìn)程的傳統(tǒng)并行程序相比早芭,提高了性能彼城。
- 允許編寫利用最大 CPU 時(shí)間的有效程序
- 提高復(fù)雜應(yīng)用程序或程序的響應(yīng)能力。
- 增加 CPU 資源的使用并降低維護(hù)成本。
- 節(jié)省時(shí)間和并行任務(wù)募壕。
- 如果在單個(gè)線程中發(fā)生異常调炬,它不會(huì)影響其他線程,因?yàn)榫€程是獨(dú)立的舱馅。
- 與同時(shí)執(zhí)行多個(gè)進(jìn)程相比缰泡,資源密集型更少。
2. Java中的線程是什么代嗤?
線程基本上是可以由調(diào)度程序獨(dú)立管理的輕量級(jí)和最小的處理單元棘钞。線程被稱為進(jìn)程的一部分,它只是讓程序與進(jìn)程的其他部分或線程同時(shí)有效地執(zhí)行干毅。使用線程宜猜,可以以最簡(jiǎn)單的方式執(zhí)行復(fù)雜的任務(wù)。它被認(rèn)為是利用機(jī)器中可用的多個(gè) CPU 的最簡(jiǎn)單方法硝逢。它們共享公共地址空間并且彼此獨(dú)立姨拥。
3、Java實(shí)現(xiàn)線程的兩種方式是什么渠鸽?
在java中實(shí)現(xiàn)線程基本上有兩種方法叫乌,如下所示:
- 擴(kuò)展線程類
例子:
class MultithreadingDemo extends Thread
{
public void run()
{
System.out.println("My thread is in running state.");
}
public static void main(String args[])
{
MultithreadingDemoobj=new MultithreadingDemo();
obj.start();
}
}
輸出:
My thread is in running state.
- 在 Java 中實(shí)現(xiàn)Runnable接口
示例
class MultithreadingDemo implements Runnable
{
public void run()
{
System.out.println("My thread is in running state.");
}
public static void main(String args[])
{
MultithreadingDemo obj=new MultithreadingDemo();
Threadtobj =new Thread(obj); tobj.start();
}
}
輸出:
My thread is in running state.
4、線程和進(jìn)程有什么區(qū)別拱绑?
線程:它只是指特定進(jìn)程的最小單元。它具有同時(shí)執(zhí)行程序的不同部分(稱為線程)的能力丽蝎。
進(jìn)程:它只是指正在執(zhí)行的程序猎拨,即活動(dòng)程序⊥雷瑁可以使用 PCB(過程控制塊)處理過程红省。
線 | 過程 |
---|---|
它是進(jìn)程子單元的子集。 | 它是一個(gè)包含多個(gè)線程的正在執(zhí)行的程序国觉。 |
在這種情況下吧恃,線程間通信更快、更便宜麻诀、更容易痕寓、更高效,因?yàn)榫€程共享它們所屬進(jìn)程的相同內(nèi)存地址蝇闭。 | 在這種情況下呻率,進(jìn)程間通信更慢、更昂貴呻引、更復(fù)雜礼仗,因?yàn)槊總€(gè)進(jìn)程都有不同的內(nèi)存空間或地址。 |
這些更容易創(chuàng)建、輕量級(jí)并且開銷更少元践。 | 這些很難創(chuàng)建韭脊,重量級(jí),并且有更多的開銷单旁。 |
它需要更少的時(shí)間來創(chuàng)建沪羔、終止和上下文切換。 | 它需要更多的時(shí)間來創(chuàng)建慎恒、終止和上下文切換任内。 |
具有多個(gè)線程的進(jìn)程使用較少的資源。 | 沒有線程的進(jìn)程使用更多資源融柬。 |
線程是進(jìn)程的一部分死嗦,因此它們相互依賴,但每個(gè)線程獨(dú)立執(zhí)行粒氧。 | 進(jìn)程相互獨(dú)立越除。 |
需要在線程中進(jìn)行同步以避免意外情況或問題。 | 每個(gè)進(jìn)程都不需要同步外盯。 |
他們彼此共享數(shù)據(jù)和信息摘盆。 | 他們不相互共享數(shù)據(jù)。 |
5. 類鎖和對(duì)象鎖有什么區(qū)別饱苟?
類鎖:在java中孩擂,每個(gè)類都有一個(gè)唯一的鎖,通常稱為類級(jí)鎖箱熬。這些鎖是使用關(guān)鍵字“靜態(tài)同步”實(shí)現(xiàn)的类垦,可用于使靜態(tài)數(shù)據(jù)線程安全。它通常在想要防止多個(gè)線程進(jìn)入同步塊時(shí)使用城须。
例子:
public class ClassLevelLockExample
{
public void classLevelLockMethod()
{
synchronized (ClassLevelLockExample.class)
{
//DO your stuff here
}
}
}
對(duì)象鎖:在java中蚤认,每個(gè)對(duì)象都有一個(gè)唯一的鎖,通常稱為對(duì)象級(jí)鎖糕伐。這些鎖是使用關(guān)鍵字“synchronized”實(shí)現(xiàn)的砰琢,可用于保護(hù)非靜態(tài)數(shù)據(jù)。它通常在想要同步非靜態(tài)方法或塊時(shí)使用良瞧,以便只有線程能夠在給定的類實(shí)例上執(zhí)行代碼塊陪汽。
示例 :
public class ObjectLevelLockExample
{
public void objectLevelLockMethod()
{
synchronized (this)
{
//DO your stuff here
}
}
}
6、User線程和Daemon線程有什么區(qū)別褥蚯?
用戶和守護(hù)進(jìn)程基本上是 Java 中使用“線程類”的兩種類型的線程掩缓。
用戶線程(非守護(hù)線程):在 Java 中,用戶線程具有特定的生命周期遵岩,其生命周期獨(dú)立于任何其他線程你辣。JVM(Java 虛擬機(jī))在終止之前等待任何用戶線程完成其任務(wù)巡通。當(dāng)用戶線程完成時(shí),JVM 會(huì)終止整個(gè)程序以及相關(guān)的守護(hù)線程舍哄。
守護(hù)線程:在Java中宴凉,守護(hù)線程基本上被稱為服務(wù)提供者,為用戶線程提供服務(wù)和支持表悬。守護(hù)線程的線程類中基本上有兩種可用的方法:setDaemon() 和 isDaemon()弥锄。
用戶線程與守護(hù)線程
用戶線程 | 守護(hù)線程 |
---|---|
JVM 在終止前等待用戶線程完成其任務(wù)。 | JVM 在終止前不會(huì)等待守護(hù)線程完成其任務(wù)蟆沫。 |
這些線程通常由用戶創(chuàng)建籽暇,用于并發(fā)執(zhí)行任務(wù)。 | 這些線程通常由 JVM 創(chuàng)建饭庞。 |
它們用于應(yīng)用程序的關(guān)鍵任務(wù)或核心工作戒悠。 | 它們不用于任何關(guān)鍵任務(wù),而是用于執(zhí)行一些輔助任務(wù)舟山。 |
這些線程被稱為高優(yōu)先級(jí)任務(wù)绸狐,因此需要在前臺(tái)運(yùn)行。 | 這些線程被稱為低優(yōu)先級(jí)線程累盗,因此特別需要支持后臺(tái)任務(wù)寒矿,如垃圾收集、釋放未使用對(duì)象的內(nèi)存等若债。 |
7.我們?nèi)绾蝿?chuàng)建守護(hù)線程符相?
我們可以使用線程類setDaemon(true)在 java 中創(chuàng)建守護(hù)線程。它用于將當(dāng)前線程標(biāo)記為守護(hù)線程或用戶線程蠢琳。isDaemon()方法一般用于檢查當(dāng)前線程是否為守護(hù)進(jìn)程啊终。如果線程是守護(hù)進(jìn)程,它將返回 true挪凑,否則返回 false孕索。
示例
**說明 setDaemon() 和 isDaemon() 方法使用的程序逛艰。 **
public class DaemonThread extends Thread
{
public DaemonThread(String name){
super(name);
}
public void run()
{
// Checking whether the thread is Daemon or not
if(Thread.currentThread().isDaemon())
{
System.out.println(getName() + " is Daemon thread");
}
else
{
System.out.println(getName() + " is User thread");
}
}
public static void main(String[] args)
{
DaemonThread t1 = new DaemonThread("t1");
DaemonThread t2 = new DaemonThread("t2");
DaemonThread t3 = new DaemonThread("t3");
// Setting user thread t1 to Daemon
t1.setDaemon(true);
// starting first 2 threads
t1.start();
t2.start();
// Setting user thread t3 to Daemon
t3.setDaemon(true);
t3.start();
}
}
輸出:
t1 is Daemon thread
t3 is Daemon thread
t2 is User thread
但是只能在start() 方法之前調(diào)用 setDaemon()方法躏碳,否則肯定會(huì)拋出 IllegalThreadStateException,如下所示:
public class DaemonThread extends Thread
{
public void run()
{
System.out.println("Thread name: " + Thread.currentThread().getName());
System.out.println("Check if its DaemonThread: "
+ Thread.currentThread().isDaemon());
}
public static void main(String[] args)
{
DaemonThread t1 = new DaemonThread();
DaemonThread t2 = new DaemonThread();
t1.start();
// Exception as the thread is already started
t1.setDaemon(true);
t2.start();
}
}
輸出:
Thread name: Thread-0
Check if its DaemonThread: false
8. wait() 和 sleep() 方法是什么散怖?
wait():顧名思義菇绵,它是一種非靜態(tài)方法,它會(huì)導(dǎo)致當(dāng)前線程等待并進(jìn)入睡眠狀態(tài)镇眷,直到其他一些線程為對(duì)象的監(jiān)視器(鎖)調(diào)用 notify() 或 notifyAll() 方法咬最。它只是釋放鎖,主要用于線程間通信欠动。它在對(duì)象類中定義永乌,并且只能從同步上下文中調(diào)用惑申。
synchronized(monitor)
{
monitor.wait(); Here Lock Is Released by Current Thread
}
sleep():顧名思義鸣个,它是一種靜態(tài)方法限次,可以暫拖穹或停止當(dāng)前線程的執(zhí)行一段時(shí)間抛杨。它在等待時(shí)不會(huì)釋放鎖掸驱,主要用于在執(zhí)行時(shí)引入暫停端礼。它在線程類中定義刘莹,無需從同步上下文中調(diào)用暇藏。
synchronized(monitor)
{
Thread.sleep(1000); Here Lock Is Held by The Current Thread
//after 1000 milliseconds, the current thread will wake up, or after we call that is interrupt() method
}
9. notify() 和 notifyAll() 有什么區(qū)別橄抹?
notify():它發(fā)送一個(gè)通知并且只喚醒一個(gè)線程而不是多個(gè)線程在對(duì)象的監(jiān)視器上等待靴迫。
notifyAll():它發(fā)送通知并喚醒所有線程并允許它們競(jìng)爭(zhēng)對(duì)象的監(jiān)視器而不是單個(gè)線程。
10. 為什么 Object 類中存在 wait()楼誓、notify() 和 notifyAll() 方法玉锌?
我們知道每個(gè)對(duì)象都有一個(gè)監(jiān)視器,它允許線程持有對(duì)象上的鎖慌随。但是線程類不包含任何監(jiān)視器芬沉。線程通常通過調(diào)用對(duì)象的wait()方法等待對(duì)象的監(jiān)視器(鎖),并使用notify()或notifyAll()方法通知正在等待同一鎖的其他線程阁猜。因此丸逸,這三個(gè)方法僅在對(duì)象上調(diào)用,并允許所有線程與在該對(duì)象上創(chuàng)建的每個(gè)線程進(jìn)行通信剃袍。
11.什么是Runnable和Callable接口黄刚?寫出它們之間的區(qū)別。
這兩個(gè)接口一般都用于封裝需要由另一個(gè)線程執(zhí)行的任務(wù)民效。但是它們之間有一些區(qū)別憔维,如下所示:
運(yùn)行接口:這個(gè)接口基本上從一開始就在 Java 中可用。它僅用于在并發(fā)線程上執(zhí)行代碼畏邢。
可調(diào)用接口:這個(gè)接口基本上是作為并發(fā)包的一部分引入的新接口业扒。它解決了可運(yùn)行接口的限制以及一些重大變化,如泛型舒萎、枚舉程储、靜態(tài)導(dǎo)入、變量參數(shù)方法等臂寝。它使用泛型來定義對(duì)象的返回類型章鲤。
public interface Runnable
{
public abstract void run();
}
public interface Callable<V>
{
V call() throws Exception;
}
可運(yùn)行接口與可調(diào)用接口
可運(yùn)行接口 | 可調(diào)用接口 |
---|---|
它不返回任何結(jié)果,因此不能拋出檢查異常咆贬。 | 它返回一個(gè)結(jié)果败徊,因此可以拋出異常。 |
它不能傳遞給 invokeAll 方法掏缎。 | 它可以傳遞給 invokeAll 方法皱蹦。 |
它是在 JDK 1.0 中引入的煤杀。 | 它是在 JDK 5.0 中引入的,因此在 Java 5 之前不能使用它沪哺。 |
它只是屬于 Java.lang怜珍。 | 它只是屬于 java.util.concurrent。 |
它使用 run() 方法來定義任務(wù)凤粗。 | 它使用 call() 方法來定義任務(wù)酥泛。 |
要使用此接口,需要重寫 run() 方法嫌拣。 | 要使用此接口柔袁,需要重寫 call() 方法。 |
12异逐、Thread類的start()和run()方法是什么捶索?
start():簡(jiǎn)單來說,start() 方法用于啟動(dòng)或開始執(zhí)行新創(chuàng)建的線程灰瞻。當(dāng)調(diào)用 start() 方法時(shí)腥例,會(huì)創(chuàng)建一個(gè)新線程,并且這個(gè)新創(chuàng)建的線程會(huì)執(zhí)行保存在 run() 方法中的任務(wù)酝润。一個(gè)人只能調(diào)用一次 start() 方法燎竖。
run():簡(jiǎn)單來說,run() 方法用于啟動(dòng)或開始執(zhí)行同一個(gè)線程要销。當(dāng)調(diào)用 run() 方法時(shí)构回,不會(huì)像 start() 方法那樣創(chuàng)建新線程。該方法由當(dāng)前線程執(zhí)行疏咐∠说В可以多次調(diào)用 run() 方法。
13.解釋線程池浑塞?
線程池只是啟動(dòng)時(shí)預(yù)初始化或工作線程的集合借跪,可用于執(zhí)行任務(wù)并在完成時(shí)放回池中。它被稱為池線程酌壕,其中創(chuàng)建了一組固定大小的線程掏愁。通過減少應(yīng)用程序線程的數(shù)量并管理它們的生命周期,可以使用線程池來緩解性能問題仅孩。使用線程托猩,可以提高性能并實(shí)現(xiàn)更好的系統(tǒng)穩(wěn)定性印蓖。為了創(chuàng)建線程池辽慕,java.util.concurrent.Executors 類通常提供工廠方法。
14. join() 方法的目的是什么赦肃?
join()方法通常用于暫停當(dāng)前線程的執(zhí)行溅蛉,除非并且直到調(diào)用 join 的指定線程死亡或完成公浪。要停止一個(gè)線程運(yùn)行直到另一個(gè)線程結(jié)束,可以使用此方法船侧。它將一個(gè)線程執(zhí)行的開始連接到另一個(gè)線程執(zhí)行的結(jié)束欠气。它被認(rèn)為是線程類的最終方法。
15. 垃圾回收是什么意思镜撩?
垃圾回收基本上是一個(gè)自動(dòng)管理內(nèi)存的過程预柒。它使用了幾種 GC 算法,其中流行的一種包括 Mark 和 Sweep袁梗。該過程包括三個(gè)階段宜鸯,即標(biāo)記、刪除和壓縮/復(fù)制遮怜。簡(jiǎn)單來說淋袖,垃圾收集器找到程序不再需要的對(duì)象,然后刪除或移除這些未使用的對(duì)象以釋放內(nèi)存空間锯梁。
16. 解釋死鎖的含義以及何時(shí)會(huì)發(fā)生即碗?
死鎖,顧名思義陌凳,就是多個(gè)線程永遠(yuǎn)被阻塞的情況剥懒。它通常發(fā)生在多個(gè)線程持有不同資源的鎖并等待其他資源完成其任務(wù)時(shí)。
上圖顯示了兩個(gè)線程被永遠(yuǎn)阻塞的死鎖情況合敦。線程 1 持有對(duì)象 1 但需要對(duì)象 2 才能完成處理蕊肥,而線程 2 持有對(duì)象 2 但首先需要對(duì)象 1。在這種情況下蛤肌,它們都將永遠(yuǎn)保持鎖定并且永遠(yuǎn)不會(huì)完成任務(wù)壁却。
17.解釋Java中的volatile變量?
volatile 變量基本上是一個(gè)關(guān)鍵字裸准,用于確保和解決多線程編程中變量更改的可見性展东。此關(guān)鍵字不能與類和方法一起使用,而是可以與變量一起使用炒俱。它只是用于實(shí)現(xiàn)線程安全盐肃。如果將任何變量標(biāo)記為易失性,那么所有線程都可以直接從主存而不是 CPU 緩存中讀取其值权悟,以便每個(gè)線程都可以獲取變量的更新值砸王。
18.線程之間如何通信?
線程可以使用三種方法進(jìn)行通信峦阁,即wait()谦铃、notify() 和notifyAll()。
19榔昔、兩個(gè)線程可以同時(shí)執(zhí)行兩種方法(靜態(tài)和非靜態(tài))嗎驹闰?
對(duì)的瘪菌,這是可能的。如果兩個(gè)線程都獲得了不同對(duì)象的鎖嘹朗,那么它們可以同時(shí)執(zhí)行而不會(huì)出現(xiàn)任何問題师妙。
20. finalize() 方法的目的是什么?
Finalize() 方法基本上是 Object 類的一種方法屹培,專門用于在垃圾回收之前對(duì)非托管資源執(zhí)行清理操作默穴。它根本不打算被稱為普通方法。完成 finalize() 方法后褪秀,對(duì)象會(huì)自動(dòng)銷毀壁顶。
面向有經(jīng)驗(yàn)的 Java 中的多線程面試問題
21.什么是同步過程?為什么要使用它溜歪?
同步基本上是 java 中的一個(gè)過程若专,它啟用了一種簡(jiǎn)單的策略來避免線程干擾和內(nèi)存一致性錯(cuò)誤。此過程確保當(dāng)一個(gè)線程嘗試訪問共享資源時(shí)蝴猪,該資源一次只被一個(gè)線程使用调衰。它可以通過以下三種不同的方式實(shí)現(xiàn):
- 通過同步方法
- 通過同步塊
- 通過靜態(tài)同步
語法:
synchronized (object)
{
//statement to be synchronized
}
22. 什么是同步方法和同步塊?應(yīng)該首選哪一個(gè)自阱?
同步方法:在此方法中嚎莉,線程在進(jìn)入同步方法時(shí)獲取對(duì)象上的鎖,并在離開該方法時(shí)正撑嫱悖或通過拋出異常釋放鎖趋箩。除非當(dāng)前線程完成執(zhí)行并釋放鎖,否則其他線程不能使用整個(gè)方法加派。當(dāng)想要鎖定特定方法的全部功能時(shí)叫确,可以使用它。
Synchronized Block:在該方法中芍锦,線程在synchronized關(guān)鍵字后面的括號(hào)內(nèi)的對(duì)象上獲取鎖竹勉,并在離開塊時(shí)釋放鎖。除非同步塊存在娄琉,否則沒有其他線程可以獲取鎖定對(duì)象上的鎖次乓。當(dāng)一個(gè)人想要保持程序的其他部分可供其他線程訪問時(shí),可以使用它孽水。
同步塊應(yīng)該更受青睞票腰,因?yàn)樗梢蕴岣咛囟ǔ绦虻男阅堋K绘i定程序的某個(gè)部分(關(guān)鍵部分)而不是整個(gè)方法女气,因此導(dǎo)致爭(zhēng)用較少杏慰。
23. 什么是線程饑餓?
線程饑餓基本上是一種情況或條件,即線程無法定期訪問共享資源逃默,因此無法繼續(xù)或取得進(jìn)展。這是因?yàn)槠渌€程優(yōu)先級(jí)高簇搅,占用資源時(shí)間過長(zhǎng)完域。這通常發(fā)生在沒有獲得 CPU 以繼續(xù)執(zhí)行的低優(yōu)先級(jí)線程上。
24. 什么是活鎖瘩将?當(dāng)它發(fā)生時(shí)會(huì)發(fā)生什么吟税?
與死鎖類似,活鎖也是另一個(gè)并發(fā)問題姿现。在這種情況下肠仪,線程的狀態(tài)在彼此之間發(fā)生變化而沒有任何進(jìn)展。線程不會(huì)被阻塞备典,但由于資源不可用而停止執(zhí)行异旧。
25.什么是阻塞隊(duì)列?
BlockingQueue 基本上代表了一個(gè)線程安全的隊(duì)列提佣。生產(chǎn)者線程使用 put() 方法將資源/元素插入隊(duì)列吮蛹,除非它已滿,而消費(fèi)者線程使用 take() 方法從隊(duì)列中獲取資源拌屏,直到它變空潮针。但是如果一個(gè)線程試圖從一個(gè)空隊(duì)列中出隊(duì),那么一個(gè)特定的線程將被阻塞倚喂,直到某個(gè)其他線程將一個(gè)項(xiàng)目插入隊(duì)列,或者如果一個(gè)線程試圖將一個(gè)項(xiàng)目插入一個(gè)已經(jīng)滿的隊(duì)列端圈,那么一個(gè)特定的線程將被阻塞焦读,直到一些線程從隊(duì)列中取出一個(gè)項(xiàng)目。
示例:
package org.arpit.java2blog;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueuePCExample {
public static void main(String[] args) {
BlockingQueue<String> queue=new ArrayBlockingQueue<>(5);
Producer producer=new Producer(queue);
Consumer consumer=new Consumer(queue);
Thread producerThread = new Thread(producer);
Thread consumerThread = new Thread(consumer);
producerThread.start();
consumerThread.start();
}
static class Producer implements Runnable {
BlockingQueue<String> queue=null;
public Producer(BlockingQueue queue) {
super();
this.queue = queue;
}
@Override
public void run() {
try {
System.out.println("Producing element 1");
queue.put("Element 1");
Thread.sleep(1000);
System.out.println("Producing element 2");
queue.put("Element 2");
Thread.sleep(1000);
System.out.println("Producing element 3");
queue.put("Element 3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Consumer implements Runnable {
BlockingQueue<String> queue=null;
public Consumer(BlockingQueue queue) {
super();
this.queue = queue;
}
@Override
public void run() {
while(true)
{
try {
System.out.println("Consumed "+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
輸出:
Producing element 1
Consumed Element 1
Producing element 2
Consumed Element 2
Producing element 3
Consumed Element 3
26. 一個(gè)線程可以啟動(dòng)兩次嗎舱权?
不吨灭,一旦線程啟動(dòng)并完成其執(zhí)行,就根本不可能重新啟動(dòng)線程刑巧。線程只運(yùn)行一次喧兄,如果您嘗試第二次運(yùn)行它,那么它將拋出運(yùn)行時(shí)異常啊楚,即 java.lang.IllegalThreadStateException吠冤。
示例:
public class TestThreadTwice1 extends Thread{
public void run(){
System.out.println(" thread is executing now........");
}
public static void main(String args[]){
TestThreadTwice1 t1=new TestThreadTwice1();
t1.start();
t1.start();
}
}
輸出:
thread is executing now........
Exception in thread "main" java.lang.IllegalThreadStateException
27. 解釋上下文切換。
上下文切換基本上是多線程的一個(gè)重要特性恭理。它被稱為 CPU 從一個(gè)線程或進(jìn)程切換到另一個(gè)線程或進(jìn)程拯辙。它允許多個(gè)進(jìn)程共享同一個(gè) CPU。在上下文切換中,存儲(chǔ)線程或進(jìn)程的狀態(tài)涯保,以便稍后可以在需要時(shí)恢復(fù)線程的執(zhí)行诉濒。
28. 什么是 CyclicBarrier 和 CountDownLatch?
CyclicBarrier 和 CountDownLatch 都是管理多線程編程所必需的夕春。但是它們之間有一些區(qū)別未荒,如下所示:
CyclicBarrier:它是一種使用某種算法同步線程處理的工具。它使一組線程相互等待及志,直到它們到達(dá)共同的執(zhí)行點(diǎn)或共同的障礙點(diǎn)片排,然后讓它們進(jìn)一步繼續(xù)執(zhí)行。即使通過設(shè)置屏障破壞了屏障速侈,也可以重復(fù)使用相同的 CyclicBarrier率寡。
CountDownLatch:它是一個(gè)工具,它可以讓主線程等待倚搬,直到強(qiáng)制操作被其他線程執(zhí)行并完成冶共。簡(jiǎn)而言之,它確保一個(gè)線程在開始執(zhí)行之前等待另一個(gè)線程中的執(zhí)行完成每界。一旦計(jì)數(shù)達(dá)到 0比默,就不能重用相同的 CountDownLatch。
29. 線程間通信是什么意思盆犁?
線程間通信命咐,顧名思義,是一種進(jìn)程或機(jī)制谐岁,多個(gè)線程可以使用該進(jìn)程或機(jī)制相互通信醋奠。它特別用于避免 java 中的線程輪詢,可以使用 wait()伊佃、notify() 和 notifyAll() 方法獲得窜司。
30. 什么是線程調(diào)度器和時(shí)間片?
線程調(diào)度器:它是 JVM 的一個(gè)組件航揉,用于在多個(gè)線程等待獲得執(zhí)行機(jī)會(huì)的情況下決定下一個(gè)線程將執(zhí)行塞祈。通過查看分配給每個(gè) READY 線程的優(yōu)先級(jí),線程調(diào)度程序選擇下一個(gè)運(yùn)行來執(zhí)行帅涂。線程調(diào)度主要使用兩種機(jī)制:搶占式調(diào)度和時(shí)間片調(diào)度议薪。
Time Slicing:它特別用于劃分CPU時(shí)間并將它們分配給活動(dòng)線程。在這種情況下媳友,每個(gè)線程將獲得一個(gè)預(yù)定義的時(shí)間片來執(zhí)行斯议。當(dāng)時(shí)間到期時(shí),特定線程必須等到其他線程有機(jī)會(huì)以循環(huán)方式使用它們的時(shí)間醇锚。每個(gè)正在運(yùn)行的線程都會(huì)在固定的時(shí)間段內(nèi)執(zhí)行哼御。
31.什么是關(guān)機(jī)鉤子?
關(guān)閉掛鉤只是在 JVM 關(guān)閉之前隱式調(diào)用的線程。它是 JVM 最重要的特性之一恋昼,因?yàn)樗峁┝诉M(jìn)行資源清理或保存 JVM 關(guān)閉的應(yīng)用程序狀態(tài)的能力看靠。通過調(diào)用 Runtime 類的 halt(int) 方法,可以停止關(guān)閉鉤子矩屁。使用以下方法空幻,可以添加關(guān)閉掛鉤鬓梅。
public void addShutdownHook(Thread hook){}
Runtime r=Runtime.getRuntime();
r.addShutdownHook(new MyThread());
32.什么忙轉(zhuǎn)活孩?
Busy Spinning崔涂,也稱為Busy-waiting,是一種線程等待某些條件發(fā)生的技術(shù)忧额,而不調(diào)用等待或睡眠方法并釋放CPU示启。在這種情況下谎仲,可以通過讓它運(yùn)行一個(gè)空循環(huán)一段時(shí)間來暫停一個(gè)線程旦部,它甚至不給 CPY 控制權(quán)。因此士八,它用于保留 CPU 緩存并避免重建緩存的成本醋虏。
33霞揉、什么是ConcurrentHashMap和Hashtable绊序?在java中,為什么ConcurrentHashMap被認(rèn)為比Hashtable快秽荞?
ConcurrentHashMap:它是在 Java 1.5 中引入的骤公,用于使用多個(gè)存儲(chǔ)桶存儲(chǔ)數(shù)據(jù)。顧名思義扬跋,它允許對(duì)地圖進(jìn)行并發(fā)讀寫操作阶捆。它只在進(jìn)行迭代時(shí)鎖定映射的特定部分以提供線程安全,以便其他讀取器仍然可以訪問映射而無需等待迭代完成钦听。
Hashtable:它是一個(gè)線程安全的遺留類洒试,在舊版本的 java 中引入,用于使用哈希表存儲(chǔ)鍵或值對(duì)朴上。與 ConcurrentHashMap 不同垒棋,它不提供任何無鎖讀取。它只是在進(jìn)行迭代時(shí)鎖定整個(gè)地圖痪宰。
ConcurrentHashMap 和 Hashtable 都是線程安全的叼架,但與 Hashtable 不同畔裕,ConcurrentHashMap 通常會(huì)避免讀鎖并提高性能。與 Hashtable 不同乖订,ConcurrentHashMap 還提供無鎖讀取柴钻。因此,ConcurrentHashMap 被認(rèn)為比 Hashtable 更快垢粮,尤其是當(dāng)讀取器的數(shù)量比寫入器的數(shù)量多時(shí)贴届。
34.解釋線程優(yōu)先級(jí)。
線程優(yōu)先級(jí)僅僅意味著具有最高優(yōu)先級(jí)的線程將在低優(yōu)先級(jí)線程之前獲得執(zhí)行的機(jī)會(huì)蜡吧『硫荆可以指定優(yōu)先級(jí),但最高優(yōu)先級(jí)線程不必在低優(yōu)先級(jí)線程之前執(zhí)行昔善。線程調(diào)度程序根據(jù)線程優(yōu)先級(jí)將處理器分配給線程元潘。優(yōu)先級(jí)范圍從最低優(yōu)先級(jí)到最高優(yōu)先級(jí)在 1-10 之間變化。
35. Java中的ThreadLocal變量是什么意思君仆?
ThreadLocal 變量是由 Java ThreadLocal 類創(chuàng)建和提供的特殊類型的變量翩概。這些變量只允許被同一個(gè)線程讀寫靴跛。兩個(gè)線程無法看到彼此的 ThreadLocal 變量叮喳,因此即使它們執(zhí)行相同的代碼,也不會(huì)出現(xiàn)任何競(jìng)爭(zhēng)條件歼指,并且代碼將是線程安全的咖摹。
示例:
public class ThreadLocalExp
{
public static class MyRunnable implements Runnable
{
private ThreadLocal<Integer> threadLocal =
new ThreadLocal<Integer>();
@Override
public void run() {
threadLocal.set( (int) (Math.random() * 50D) );
try
{
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println(threadLocal.get());
}
}
public static void main(String[] args)
{
MyRunnable runnableInstance = new MyRunnable();
Thread t1 = new Thread(runnableInstance);
Thread t2 = new Thread(runnableInstance);
// this will call run() method
t1.start();
t2.start();
}
}
輸出:
10
33
10 33
36.什么是信號(hào)量评姨?
信號(hào)量被認(rèn)為是一種線程同步結(jié)構(gòu),通常需要使用計(jì)數(shù)器來控制和管理對(duì)共享資源的訪問萤晴。它只是設(shè)置線程的限制吐句。信號(hào)量類在包 java.util.concurrent 中定義,可用于在線程之間發(fā)送信號(hào)以避免丟失信號(hào)或保護(hù)臨界區(qū)店读。它還可以用于實(shí)現(xiàn)資源池或有界集合嗦枢。
37. 解釋線程組。為什么我們不應(yīng)該使用它屯断?
ThreadGroup 是一個(gè)用于在單個(gè)對(duì)象中創(chuàng)建多組線程的類文虏。這組線程以三種結(jié)構(gòu)的形式存在,其中每個(gè)線程組都有一個(gè)父線程裹纳,但初始線程除外择葡。線程組也可以包含其他線程組。一個(gè)線程只被允許訪問關(guān)于它自己的線程組的信息剃氧,而不是其他線程組敏储。
以前在舊版本的 Java 中,唯一沒有線程組就無法工作的功能是 uncaughtException(Thread t, Throwable e)朋鞍。但是現(xiàn)在在 Java 5 版本中已添,有了 Thread.setUncaughtExceptionHandler(UncaughtExceptionHandler)妥箕。所以現(xiàn)在即使沒有線程組也可以工作畦幢,因此不需要使用線程組宇葱。
t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler()
{
@Override
public void uncaughtException(Thread t, Throwable e)
{
System.out.println("exception occured:"+e.getMessage());
}
};
38刊头、什么是ExecutorService接口黍瞧?
ExecutorService 接口基本上是 Executor 接口的子接口,具有一些額外的方法或特性原杂,有助于管理和控制線程的執(zhí)行印颤。它使我們能夠在線程上異步執(zhí)行任務(wù)。
例子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
ExecutorService e = Executors.newSingleThreadExecutor();
try {
e.submit(new Thread());
System.out.println("Shutdown executor");
e.shutdown();
e.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException ex) {
System.err.println("tasks interrupted");
} finally {
if (!e.isTerminated()) {
System.err.println("cancel non-finished tasks");
}
e.shutdownNow();
System.out.println("shutdown finished");
}
}
static class Task implements Runnable {
public void run() {
try {
Long duration = (long) (Math.random() * 20);
System.out.println("Running Task!");
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
輸出:
Shutdown executor
shutdown finished
39穿肄、如果我們不重寫線程類的run()方法會(huì)發(fā)生什么年局?
如果我們不覆蓋 run() 方法,什么都不會(huì)發(fā)生咸产。編譯器不會(huì)顯示任何錯(cuò)誤矢否。它將執(zhí)行線程類的 run() 方法,我們不會(huì)得到任何輸出锐朴,因?yàn)?run() 方法的實(shí)現(xiàn)是空的兴喂。
**例子: **
class MyThread extends Thread {
//don't override run() method
}
public class DontOverrideRun {
public static void main(String[] args) {
System.out.println("Started Main.");
MyThread thread1=new MyThread();
thread1.start();
System.out.println("Ended Main.");
}
}
輸出:
Started Main.
Ended Main.
40.什么是鎖接口蔼囊?為什么使用鎖接口比使用同步塊更好焚志。?
Lock 接口是在 Java 1.5 中引入的膳沽,一般用作同步機(jī)制,為阻塞提供重要的操作痛阻。
使用 Lock 接口優(yōu)于 Synchronization 塊的優(yōu)點(diǎn):
- Lock 接口的方法俏扩,即 Lock() 和 Unlock() 可以在不同的方法中調(diào)用。這是鎖接口相對(duì)于同步塊的主要優(yōu)勢(shì)嫉戚,因?yàn)橥綁K完全包含在單個(gè)方法中。
- 與同步塊不同凤覆,鎖接口更靈活,確保等待時(shí)間最長(zhǎng)的線程獲得公平的執(zhí)行機(jī)會(huì)。
41略号、是否可以直接調(diào)用run()方法啟動(dòng)一個(gè)新線程?
不羽利,這根本不可能。您需要調(diào)用 start 方法來創(chuàng)建新線程匾浪,否則 run 方法不會(huì)創(chuàng)建新線程。相反捌年,它將在當(dāng)前線程中執(zhí)行。
42. 在多線程編程中托酸,每個(gè)線程都可以有自己的棧嗎?
當(dāng)然应结,這是可能的。在多線程編程中,每個(gè)線程在內(nèi)存中維護(hù)自己獨(dú)立的堆棧區(qū)域玷坠,因此每個(gè)線程彼此獨(dú)立而不是相互依賴。
結(jié)論
43. 結(jié)論
總的來說秕重,多線程是 Java 和現(xiàn)代軟件開發(fā)的一個(gè)非常重要的部分服鹅。這對(duì)于提高程序的效率非常有幫助,同時(shí)也減少了存儲(chǔ)資源的使用仗哨。在本文中萨醒,我們討論了與多線程相關(guān)的重要面試問題以及面試中最常被問到的答案,并將幫助您破解面試晓褪。
推薦教程:
練習(xí)
Java 開發(fā)人員技能