更新時(shí)間:2021-05-28 11:02:24 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽1213次
1.什么是進(jìn)程?線程?區(qū)別?
(1)進(jìn)程是一個(gè)獨(dú)立的運(yùn)行環(huán)境,它可以被看作是一個(gè)程序或者一個(gè)應(yīng)用。而線程是在進(jìn)程中執(zhí)行的一個(gè)任務(wù)。eg:打開(kāi)360安全衛(wèi)士,它本身是一個(gè)程序,也是一個(gè)進(jìn)程,它里面有殺毒,清理垃圾,電腦加速等功能,當(dāng)你點(diǎn)擊殺毒的時(shí)候,殺毒任務(wù)就相當(dāng)于一個(gè)線程。
(2)進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的基本單位,而線程是操作系統(tǒng)進(jìn)行調(diào)度的基本單位。
(3)進(jìn)程讓操作系統(tǒng)的并發(fā)性成為可能,而線程讓進(jìn)程的內(nèi)部并發(fā)成為可能。
補(bǔ)充:
進(jìn)程與線程的區(qū)別 --https://jingyan.baidu.com/article/624e74598efcc834e9ba5a66.html
5個(gè)步驟,教你瞬間明白線程和線程安全
-- https://baijiahao.baidu.com/s?id=1610396903519844310&wfr=spider&for=pc
2.什么叫線程安全?
一個(gè)類或者程序所提供的接口對(duì)于線程來(lái)說(shuō)是原子操作或者多個(gè)線程之間的切換不會(huì)導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性,也就是說(shuō)我們不用考慮同步的問(wèn)題。
出現(xiàn)線程不安全的原因是什么?
如果我們創(chuàng)建的多個(gè)線程,存在著共享數(shù)據(jù),那么就有可能出現(xiàn)線程的安全問(wèn)題:當(dāng)其中一個(gè)線程操作共享數(shù)據(jù)時(shí),還未操作完成,另外的線程就參與進(jìn)來(lái),導(dǎo)致對(duì)共享數(shù)據(jù)的操作出現(xiàn)問(wèn)題。
線程不安全解決辦法?
要求一個(gè)線程操作共享數(shù)據(jù)時(shí),只有當(dāng)其完成操作共享數(shù)據(jù),其它線程才有機(jī)會(huì)執(zhí)行共享數(shù)據(jù)。java提供了兩種方式來(lái)實(shí)現(xiàn)同步互斥訪問(wèn):synchronized和Lock。
3.創(chuàng)建線程的方式?區(qū)別?
(1)繼承Thread類;
(2)實(shí)現(xiàn)Runnable接口;
(3)實(shí)現(xiàn)Callable接口;
(4)通過(guò)線程池創(chuàng)建線程;
區(qū)別
Java中,類僅支持單繼承,如果一個(gè)類繼承了Thread類,就無(wú)法再繼承其它類,因此,如果一個(gè)類既要繼承其它的類,又必須創(chuàng)建為一個(gè)線程,就可以使用實(shí)現(xiàn)Runable接口的方式。
使用實(shí)現(xiàn)Runable接口的方式創(chuàng)建的線程可以處理同一資源,實(shí)現(xiàn)資源的共享。
使用實(shí)現(xiàn)Callable接口的方式創(chuàng)建的線程,可以獲取到線程執(zhí)行的返回值、是否執(zhí)行完成等信息。
4.Java中Runnable和Callable有什么不同?
Runnable和Callable都是創(chuàng)建線程的方式。Runnable從JDK1.0開(kāi)始就有了,Callable是在JDK1.5增加的。
(1)實(shí)現(xiàn)Callable接口的任務(wù)線程能返回執(zhí)行結(jié)果;而實(shí)現(xiàn)Runnable接口的任務(wù)線程不能返回結(jié)果;
(2)Callable接口的call()方法允許拋出異常;而Runnable接口的run()方法的異常只能在內(nèi)部消化,不能繼續(xù)上拋;
5.Thread 類中的start() 和run() 方法有什么區(qū)別?
介紹說(shuō)明
(1)start() :它的作用是啟動(dòng)一個(gè)新線程。通過(guò)start()方法來(lái)啟動(dòng)的新線程,處于就緒(可運(yùn)行)狀態(tài),并沒(méi)有運(yùn)行,一旦得到cpu時(shí)間片,就開(kāi)始執(zhí)行相應(yīng)線程的run()方法,這里方法run()稱為線程體,它包含了要執(zhí)行的這個(gè)線程的內(nèi)容,run方法運(yùn)行結(jié)束,此線程隨即終止。
(2)run():就和普通的成員方法一樣,可以被重復(fù)調(diào)用。如果直接調(diào)用run方法,并不會(huì)啟動(dòng)新線程!程序中依然只有主線程這一個(gè)線程,其程序執(zhí)行路徑還是只有一條,還是要順序執(zhí)行。
start和run區(qū)別
(1)start() 可以啟動(dòng)一個(gè)新線程,run()不能。
(2)start()不能被重復(fù)調(diào)用,run()可以。
(3)start()中的run代碼可以不執(zhí)行完就繼續(xù)執(zhí)行下面的代碼,即進(jìn)行了線程切換。直接調(diào)用run方法必須等待其代碼全部執(zhí)行完才能繼續(xù)執(zhí)行下面的代碼。
(4)start() 實(shí)現(xiàn)了多線程,run()沒(méi)有實(shí)現(xiàn)多線程。
6.Java多線程中調(diào)用wait() 和 sleep()方法有什么不同?
sleep()和wait()都是使線程暫停執(zhí)行一段時(shí)間的方法。二者區(qū)別為:
(1)原理不同(面試時(shí)可不答,偏重2,3)
sleep()方法是Thread類的靜態(tài)方法,是線程用來(lái)控制自身流程的。
而wait()方法是Object類的方法,用于線程間的通信。
(2)對(duì)鎖的處理機(jī)制不同
調(diào)用wait()的時(shí)候方法會(huì)釋放當(dāng)前持有的鎖,而sleep方法不會(huì)釋放鎖。
(3)使用地方不同
sleep方法則可以放在任何地方使用,而wait()方法必須放在同步方法或者同步代碼塊中使用。
sleep()方法必須捕獲異常,而wait()、notify()、notifyAll()不需要捕獲異常。
推薦:由于sleep不會(huì)釋放鎖標(biāo)志,容易導(dǎo)致死鎖問(wèn)題的發(fā)生,一般情況下,不推薦使用sleep()方法,而推薦使用wait()方法。
7.介紹下CAS
CAS(Compare and Swap),比較與交換,實(shí)現(xiàn)并發(fā)算法時(shí)常用到的一種技術(shù),是Java保證原子性的一種重要方法,也是一種樂(lè)觀鎖的實(shí)現(xiàn)方式。CAS有3個(gè)參數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則什么都不做。
8.說(shuō)說(shuō)對(duì)于 synchronized 關(guān)鍵字的了解?
synchronized關(guān)鍵字解決的是多個(gè)線程之間訪問(wèn)資源的同步性;
synchronized 關(guān)鍵字可以保證被它修飾的方法或者代碼塊在任意時(shí)刻只能有一個(gè)線程執(zhí)行。
synchronized保證了對(duì)變量操作的可見(jiàn)性,原子性和有序性。
synchronized 的使用方式有三種:
(1)修飾同步代碼塊
(2)修飾非靜態(tài)(實(shí)例)的方法
(3)修飾靜態(tài)的方法
eg;雙重校驗(yàn)鎖實(shí)現(xiàn)單例模式使用到了synchronized
9.Lock和synchronized的區(qū)別
(1)synchronized是Java中的關(guān)鍵字,在JVM層面,而Lock是一個(gè)接口;
(2)synchronized會(huì)自動(dòng)釋放線程占有的鎖,而Lock需要主動(dòng)通過(guò)unLock()去釋放鎖,否則可能造成死鎖現(xiàn)象。
(3)使用synchronized時(shí),等待的線程會(huì)一直等待下去,不能夠響應(yīng)中斷,而Lock可以讓等待鎖的線程響應(yīng)中斷;
(4)通過(guò)Lock可以判斷鎖狀態(tài),即是否成功獲取鎖,而synchronized無(wú)法判斷。
(5)Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。
說(shuō)明:在性能上來(lái)說(shuō),如果競(jìng)爭(zhēng)資源不激烈,兩者的性能是差不多的,而當(dāng)競(jìng)爭(zhēng)資源非常激烈時(shí)(即有大量線程同時(shí)競(jìng)爭(zhēng)),此時(shí)Lock的性能要遠(yuǎn)遠(yuǎn)優(yōu)于synchronized。所以說(shuō),在具體使用時(shí)要根據(jù)適當(dāng)情況。
10.synchronized和Lock底層實(shí)現(xiàn)?
synchronized用的鎖是存在java對(duì)象里的,通過(guò)對(duì)代碼反編譯,可以看出被synchronized修飾的代碼塊,在執(zhí)行之前先使用monitorenter指令加鎖,然后在執(zhí)行結(jié)束之后再使用monitorexit指令釋放鎖資源,在整個(gè)執(zhí)行期間此代碼都是鎖定的狀態(tài),這就是典型悲觀鎖的實(shí)現(xiàn)流程。
lock鎖使用的是CAS和volatile來(lái)實(shí)現(xiàn)同步的,CAS使用硬件命令實(shí)現(xiàn)緩存一致性保證了原子性,volatile保證了可見(jiàn)性,多線程環(huán)境下所有的線程通過(guò)CAS進(jìn)行競(jìng)爭(zhēng)資源,只能有一個(gè)成功,其它的都會(huì)自旋。
11.在多線程中,什么是上下文切換?
上下文切換是存儲(chǔ)和恢復(fù)CPU狀態(tài)的過(guò)程,它使得線程執(zhí)行能夠從中斷點(diǎn)恢復(fù)執(zhí)行。是多任務(wù)操作系統(tǒng)和多線程環(huán)境的基本特征。
12.并發(fā)編程三要素
(1)原子性:程序中的所有操作是不可中斷的,要么全部執(zhí)行成功要么全部執(zhí)行失敗。
(2)有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。(處理器可能會(huì)對(duì)指令進(jìn)行重排序)
(3)可見(jiàn)性:當(dāng)多個(gè)線程訪問(wèn)同一個(gè)變量時(shí),如果其中一個(gè)線程對(duì)其作了修改,其他線程能立即獲取到最新的值。
13.Java中堆和棧有什么不同?(相對(duì)于線程來(lái)說(shuō))
棧是一塊和線程緊密相關(guān)的內(nèi)存區(qū)域。每個(gè)線程都有自己的棧內(nèi)存,用于存儲(chǔ)本地變量,方法參數(shù)和棧調(diào)用,一個(gè)線程中存儲(chǔ)的變量對(duì)其它線程是不可見(jiàn)的。
堆是所有線程共享的一片公用內(nèi)存區(qū)域。對(duì)象都在堆里創(chuàng)建,為了提升效率線程會(huì)從堆中弄一個(gè)緩存到自己的棧,如果多個(gè)線程使用該變量就可能引發(fā)問(wèn)題,這時(shí)volatile 變量就可以發(fā)揮作用了,它要求線程從主存中讀取變量的值。
14.什么是線程池? 為什么要使用它?
線程池(thread pool):一種線程使用模式。
創(chuàng)建線程要花費(fèi)資源和時(shí)間,如果任務(wù)來(lái)了才創(chuàng)建線程那么響應(yīng)時(shí)間會(huì)變長(zhǎng),而且一個(gè)進(jìn)程能創(chuàng)建的線程數(shù)有限。為了避免這些問(wèn)題,在程序啟動(dòng)的時(shí)候就創(chuàng)建若干線程來(lái)響應(yīng)處理,它們被稱為線程池,里面的線程叫工作線程。
線程池的好處:
(1)通過(guò)重用線程池中的線程,來(lái)減少每個(gè)線程創(chuàng)建和銷毀的性能開(kāi)銷。
(2)對(duì)線程進(jìn)行一些維護(hù)和管理,比如定時(shí)開(kāi)始,周期執(zhí)行,并發(fā)數(shù)控制等等。
15.什么是ThreadLocal?
ThreadLocal用于創(chuàng)建線程的本地變量,我們知道一個(gè)對(duì)象的所有線程共享它的全局變量,所以這些變量是非線程安全的,我們可以使用同步技術(shù)。但是當(dāng)我們不想使用同步的時(shí)候,我們可以選擇ThreadLocal變量。每個(gè)線程都會(huì)擁有他們自己的Thread變量,它們可以使用get()/set()方法去獲取他們的默認(rèn)值或者在線程內(nèi)部改變他們的值。
16.死鎖是什么?如何避免死鎖?
死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。這是一個(gè)嚴(yán)重的問(wèn)題,因?yàn)樗梨i會(huì)讓你的程序掛起無(wú)法完成任務(wù),死鎖的發(fā)生必須滿足以下四個(gè)條件:
(1)互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用。
(2)請(qǐng)求與保持條件:一個(gè)進(jìn)程因請(qǐng)求資源而阻塞時(shí),對(duì)已獲得的資源保持不放。
(3)不剝奪條件:進(jìn)程已獲得的資源,在末使用完之前,不能強(qiáng)行剝奪。
(4)循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
避免死鎖最簡(jiǎn)單的方法就是阻止循環(huán)等待條件,將系統(tǒng)中所有的資源設(shè)置標(biāo)志位、排序,規(guī)定所有的進(jìn)程申請(qǐng)資源必須以一定的順序(升序或降序)做操作來(lái)避免死鎖。
17.Thread類中的yield方法有什么作用?
Thread.yield() 方法會(huì)使當(dāng)前線程從運(yùn)行狀態(tài)變?yōu)榫途w狀態(tài),把運(yùn)行機(jī)會(huì)讓給其它相同優(yōu)先級(jí)的線程。它是一個(gè)靜態(tài)的原生(native)方法而且只保證當(dāng)前線程放棄CPU占用而不能保證使其它線程一定能占用CPU,執(zhí)行yield()的線程有可能會(huì)被再次繼續(xù)執(zhí)行的。
18.Java中notify 和 notifyAll有什么區(qū)別?
調(diào)用notify時(shí),只有一個(gè)等待線程會(huì)被喚醒而且它不能保證哪個(gè)線程會(huì)被喚醒,這取決于線程調(diào)度器。雖然如果你調(diào)用notifyAll方法,那么等待該鎖的所有線程都會(huì)被喚醒。
19.Java中interrupted 和 isInterruptedd方法的區(qū)別?
interrupted() 和 isInterrupted()的主要區(qū)別是前者會(huì)將中斷狀態(tài)清除而后者不會(huì)。
Java多線程的中斷機(jī)制是用內(nèi)部標(biāo)識(shí)來(lái)實(shí)現(xiàn)的,調(diào)用Thread.interrupt()來(lái)中斷一個(gè)線程就會(huì)設(shè)置中斷標(biāo)識(shí)為true。當(dāng)中斷線程調(diào)用靜態(tài)方法Thread.interrupted()來(lái)檢查中斷狀態(tài)時(shí),中斷狀態(tài)會(huì)被清零。而非靜態(tài)方法isInterrupted()用來(lái)查詢其它線程的中斷狀態(tài)且不會(huì)改變中斷狀態(tài)標(biāo)識(shí)。簡(jiǎn)單的說(shuō)就是任何拋出InterruptedException異常的方法都會(huì)將中斷狀態(tài)清零。無(wú)論如何,一個(gè)線程的中斷狀態(tài)有有可能被其它線程調(diào)用中斷來(lái)改變。
20.有三個(gè)線程T1,T2,T3,怎么確保它們按順序執(zhí)行?
在多線程中有多種方法讓線程按特定順序執(zhí)行,你可以用線程類的join()方法在一個(gè)線程中啟動(dòng)另一個(gè)線程,另外一個(gè)線程完成該線程繼續(xù)執(zhí)行。為了確保三個(gè)線程的順序你應(yīng)該先啟動(dòng)最后一個(gè)(T3調(diào)用T2,T2調(diào)用T1),這樣T1就會(huì)先完成而T3最后完成。
21.如何創(chuàng)建守護(hù)線程?
使用Thread類的setDaemon(true)方法可以將線程設(shè)置為守護(hù)線程,需要注意的是,需要在調(diào)用start()方法前調(diào)用這個(gè)方法,否則會(huì)拋出IllegalThreadStateException異常。
按 Ctrl+C 復(fù)制代碼按 Ctrl+C 復(fù)制代碼
前提知識(shí):
守護(hù)進(jìn)程(Daemon)是運(yùn)行在后臺(tái)的一種特殊進(jìn)程。它獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件(百度百科)。
Java線程分為兩類分別為daemon線程(守護(hù)線程)和User線程(用戶線程),在JVM啟動(dòng)時(shí)候會(huì)調(diào)用main函數(shù),main函數(shù)所在的線程是一個(gè)用戶線程,這個(gè)是我們可以看到的線程,其實(shí)JVM內(nèi)部同時(shí)還啟動(dòng)了好多守護(hù)線程,比如垃圾回收線程。那么守護(hù)線程和用戶線程有什么區(qū)別那?區(qū)別之一是當(dāng)最后一個(gè)非守護(hù)線程結(jié)束時(shí)候,JVM會(huì)正常退出,而不管當(dāng)前是否有守護(hù)線程,也就是說(shuō)守護(hù)線程是否結(jié)束并不影響JVM的退出。言外之意是只要有一個(gè)用戶線程還沒(méi)結(jié)束正常情況下JVM就不會(huì)退出。
22.什么是線程調(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)。
時(shí)間分片是指將可用的CPU時(shí)間分配給可用的Runnable線程的過(guò)程。分配CPU時(shí)間可以基于線程優(yōu)先級(jí)或者線程等待的時(shí)間。線程調(diào)度并不受到Java虛擬機(jī)控制,所以由應(yīng)用程序來(lái)控制它是更好的選擇(即最好不要讓你的程序依賴于線程的優(yōu)先級(jí))。
23.Java線程池中submit() 和 execute()方法有什么區(qū)別?
兩個(gè)方法都可以向線程池提交任務(wù),execute()方法的返回類型是void,它定義在Executor接口中, 而submit()方法可以返回持有計(jì)算結(jié)果的Future對(duì)象,它定義在ExecutorService接口中,它擴(kuò)展了Executor接口,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。
24.什么是FutureTask?
在Java并發(fā)程序中FutureTask表示一個(gè)可以取消的異步運(yùn)算。它有啟動(dòng)和取消運(yùn)算、查詢運(yùn)算是否完成和取回運(yùn)算結(jié)果等方法。只有當(dāng)運(yùn)算完成的時(shí)候結(jié)果才能取回,如果運(yùn)算尚未完成get方法將會(huì)阻塞。一個(gè)FutureTask對(duì)象可以對(duì)調(diào)用了Callable和Runnable的對(duì)象進(jìn)行包裝,由于FutureTask也是調(diào)用了Runnable接口所以它可以提交給Executor來(lái)執(zhí)行。
以上就是動(dòng)力節(jié)點(diǎn)小編介紹的"Java多線程高并發(fā)測(cè)試題",希望對(duì)大家有幫助,如有疑問(wèn),請(qǐng)?jiān)诰€咨詢,有專業(yè)老師隨時(shí)為您服務(wù)。
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
有基礎(chǔ) 直達(dá)就業(yè)
業(yè)余時(shí)間 高薪轉(zhuǎn)行
工作1~3年,加薪神器
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問(wèn)老師會(huì)電話與您溝通安排學(xué)習(xí)
初級(jí) 202925
初級(jí) 203221
初級(jí) 202629
初級(jí) 203743