<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JAVA寶典-面試題-多線程篇(含答案)

          共 8970字,需瀏覽 18分鐘

           ·

          2021-06-20 23:11



          下面是Java線程相關(guān)的高頻面試題(含答案),你可以用它來好好準(zhǔn)備面試。

          • 1.并行和并發(fā)有什么區(qū)別?

          • 2.進(jìn)程和線程的區(qū)別與聯(lián)系?

          • 3.守護(hù)線程是什么?

          • 4.創(chuàng)建線程有哪幾種方式?

          • 5.說一下 runnable 和 callable 有什么區(qū)別?

          • 6.線程有哪些狀態(tài)?

          • 7.sleep() 和 wait() 有什么區(qū)別?

          • 8.notify()和 notifyAll()有什么區(qū)別?

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

          • 10.創(chuàng)建線程池有哪幾種方式?

          • 11.Java線程池中submit() 和 execute()方法的區(qū)別

          • 12.在 java 程序中怎么保證多線程的運(yùn)行安全?

          • 13.多線程鎖的升級原理是什么?

          • 14. 什么是死鎖?什么是活鎖? 什么是線程饑餓?

          • 15.ThreadLocal 是什么?有哪些使用場景?

          • 16.說一下synchronized底層實(shí)現(xiàn)原理?

          • 17.synchronized和volatile的區(qū)別是什么?

          • 18.synchronized和Lock的區(qū)別是什么?

          • 19.說一下 你對atomic 的理解 ?

          • 20.說一下 你對Semaphore 的理解 ?

           給個(gè)關(guān)注,再給一個(gè)小紅心

          下面是答案解析:

          1.并行和并發(fā)有什么區(qū)別?

          • 并發(fā):是指多個(gè)線程任務(wù)在同一個(gè)CPU上快速地輪換執(zhí)行,由于切換的速度非???,給人的感覺就是這些線程任務(wù)是在同時(shí)進(jìn)行的,但其實(shí)并發(fā)只是一種邏輯上的同時(shí)進(jìn)行;
          • 并行:是指多個(gè)線程任務(wù)在不同CPU上同時(shí)進(jìn)行,是真正意義上的同時(shí)執(zhí)行。

          2.進(jìn)程和線程的區(qū)別與聯(lián)系?

          區(qū)別

          • 并發(fā)性:不僅進(jìn)程之間可以并發(fā)執(zhí)行,同一個(gè)進(jìn)程的多個(gè)線程之間也可并發(fā)執(zhí)行。

          • 擁有資源:進(jìn)程是擁有資源的一個(gè)獨(dú)立單位,線程不擁有系統(tǒng)資源,但可以訪問隸屬于進(jìn)程的資源。

          • 系統(tǒng)開銷:多進(jìn)程的程序要比多線程的程序健壯,但在進(jìn)程切換時(shí),耗費(fèi)資源較大,效率要差一些。

          線程和進(jìn)程在使用上各有優(yōu)缺點(diǎn):線程執(zhí)行開銷小,但不利于資源的管理和保護(hù);而進(jìn)程正相反。同時(shí),線程適合于在SMP機(jī)器上運(yùn)行,而進(jìn)程則可以跨機(jī)器遷移。

          聯(lián)系

          • 一個(gè)線程只能屬于一個(gè)進(jìn)程,而一個(gè)進(jìn)程可以有多個(gè)線程,但至少有一個(gè)線程;

          • 資源分配給進(jìn)程,同一進(jìn)程的所有線程共享該進(jìn)程的所有資源;

          • 處理機(jī)分給線程,即真正在處理機(jī)上運(yùn)行的是線程;

          • 線程在執(zhí)行過程中,需要協(xié)作同步。不同進(jìn)程的線程間要利用消息通信的辦法實(shí)現(xiàn)同步。

          3.守護(hù)線程是什么?

          • 護(hù)線程是程序運(yùn)行的時(shí)候在后臺提供一種通用服務(wù)的線程。所有用戶線程停止,進(jìn)程會停掉所有守護(hù)線程,退出程序。

          • 守護(hù)線程擁有自動結(jié)束自己生命周期的特性,而非守護(hù)線程不具備這個(gè)特點(diǎn)。

          應(yīng)用場景:

          JVM 中的垃圾回收線程就是典型的守護(hù)線程, 當(dāng) JVM 要退出時(shí),垃圾回收線程也會結(jié)束自己的生命周期.

          4.創(chuàng)建線程有哪幾種方式?

          1.繼承Thread類

          2.實(shí)現(xiàn)Runnable接口

          3.實(shí)現(xiàn)Callable接口

          4.通過線程池創(chuàng)建線程(ThreadPollExecutor,ExecutorService..)

          5.說一下 runnable 和 callable 有什么區(qū)別?

          • Runnable和Callable 都是接口,分別提供run方法和call方法

          • Runnable的run方法無返回值,Callable的call方法提供返回值來表示任務(wù)運(yùn)行結(jié)果

          • Runnable無法通過throws拋出異常,所有CheckedException必須在run方法內(nèi)部處理。Callable可直接拋出Exception異常.

          • Runnable可以作為Thread構(gòu)造器的參數(shù),通過開啟新的線程來執(zhí)行,也可以通過線程池來執(zhí)行。而Callable通過提交給線程池執(zhí)行

          6.線程有哪些狀態(tài)?

          這里要注意審題,是系統(tǒng)線程狀態(tài)還是Java中線程的狀態(tài)?

          Java中線程的狀態(tài)

          java.lang.Thread類

          public enum State {

              NEW,


              RUNNABLE,


              BLOCKED,

           
              WAITING,


              TIMED_WAITING,


              TERMINATED;
          }

          操作系統(tǒng)中線程的狀態(tài)

          • 初始狀態(tài)(NEW)

            對應(yīng) Java中的NEW

          • 可運(yùn)行狀態(tài)(READY)

            對應(yīng) Java中的 RUNNBALE 狀態(tài)

          • 運(yùn)行狀態(tài)(RUNNING)

            對應(yīng) Java中的 RUNNBALE 狀態(tài)

          • 等待狀態(tài)(WAITING)

            該狀態(tài)在 Java中被劃分為了 BLOCKED,WAITINGTIMED_WAITING 三種狀態(tài)

            當(dāng)線程調(diào)用阻塞式 API時(shí),進(jìn)程(線程)進(jìn)入等待狀態(tài),這里指的是操作系統(tǒng)層面的。從 JVM層面來說,Java線程仍然處于 RUNNABLE 狀態(tài)。

            JVM 并不關(guān)心操作系統(tǒng)線程的實(shí)際狀態(tài),從 JVM 看來,等待CPU使用權(quán)(操作系統(tǒng)狀態(tài)為可運(yùn)行態(tài))與等待 I/O(操作系統(tǒng)處于等待狀態(tài))沒有區(qū)別,都是在等待某種資源,所以都?xì)w入RUNNABLE 狀態(tài)

          • 終止?fàn)顟B(tài) (DEAD)

            對應(yīng)  TERMINATED

          7.sleep() 和 wait() 有什么區(qū)別?

          sleep()和wait()都是線程暫停執(zhí)行的方法。

          • sleep方法屬于Thread類中的靜態(tài)方法,wait屬于Object的成員方法。

          • sleep()不涉及線程通信,調(diào)用時(shí)會暫停此線程指定的時(shí)間,但監(jiān)控依然保持,不會釋放對象鎖,到時(shí)間自動恢復(fù)

          • wait() 用于線程間的通信,調(diào)用時(shí)會放棄對象鎖**,進(jìn)入**等待隊(duì)列,待調(diào)用notify()/notifyAll()喚醒指定的線程或者所有線程,才進(jìn)入對象鎖定池準(zhǔn)備重新獲得對象鎖進(jìn)入運(yùn)行狀態(tài)。

          • wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用(使用范圍)

          • sleep()方法必須捕獲異常InterruptedException,而wait()\notify()以及notifyAll()不需要捕獲異常。

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

          • notify() 方法隨機(jī)喚醒對象的等待池中的一個(gè)線程,進(jìn)入鎖池

          • notifyAll() 喚醒對象的等待池中的所有線程,進(jìn)入鎖池。

          • 等待池:假設(shè)線程A調(diào)用了某個(gè)對象的wait()方法,線程A就會釋放該對象的鎖,并進(jìn)入該對象的等待池,等待池中的線程不會去競爭該對象的鎖。
          • 鎖池:只有獲取了對象的鎖,線程才能執(zhí)行對象的 synchronized 代碼,對象的鎖每次只有一個(gè)線程可以獲得,其他線程只能在鎖池中等待

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

          • 調(diào)用 start() 方法是用來啟動線程的,輪到該線程執(zhí)行時(shí),會自動調(diào)用 run();

          • 調(diào)用 run() 方法,無法達(dá)到啟動多線程的目的,相當(dāng)于主線程線性執(zhí)行 Thread 對象的 run() 方法。

          • 一個(gè)線程對線的 start() 方法只能調(diào)用一次,多次調(diào)用會拋出 java.lang.IllegalThreadStateException 異常;而run() 方法沒有限制。

          10.創(chuàng)建線程池有哪幾種方式?

          • 通過Executors工廠方法創(chuàng)建   (阿里巴巴開發(fā)規(guī)約中不建議使用此種方式創(chuàng)建線程池)
          • 通過new  ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue)   自定義創(chuàng)建(推薦)

          11.Java線程池中submit() 和 execute()方法的區(qū)別

          兩個(gè)方法都可以向線程池提交任務(wù)

          • execute()方法的返回類型是void,它定義在Executor接口中。

          • submit()方法可以返回持有計(jì)算結(jié)果的Future對象,它定義在ExecutorService接口中,它擴(kuò)展了Executor接口,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。

          12.在 java 程序中怎么保證多線程的運(yùn)行安全?

          程序中保證多線程運(yùn)行安全的方式:

          1.使用安全類,比如 Java. util. concurrent 下的類。

          2.使用自動鎖 synchronized。

          3.使用手動鎖 Lock  例如Reentrantlock。

          4.保證一個(gè)或者多個(gè)操作在CPU執(zhí)行的過程中不被中斷。(原子性)

          5.保證一個(gè)線程對共享變量的修改,另外一個(gè)線程能夠立刻看到。(可見性)

          6.保證程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。(有序性)

          注意回答中不能缺少這3種特性

          線程的安全性問題體現(xiàn)在:

          • 原子性:一個(gè)或者多個(gè)操作在 CPU 執(zhí)行的過程中不被中斷的特性
          • 可見性:一個(gè)線程對共享變量的修改,另外一個(gè)線程能夠立刻看到
          • 有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行

          13.多線程鎖的升級原理是什么?

          鎖的級別從低到高:

          無鎖 -> 偏向鎖 -> 輕量級鎖 -> 重量級鎖

          鎖分級別原因:

          沒有優(yōu)化以前,synchronized是重量級鎖(悲觀鎖),使用 wait 和 notify、notifyAll 來切換線程狀態(tài)非常消耗系統(tǒng)資源;線程的掛起和喚醒間隔很短暫,這樣很浪費(fèi)資源,影響性能。所以 JVM 對 synchronized 關(guān)鍵字進(jìn)行了優(yōu)化,把鎖分為 無鎖、偏向鎖、輕量級鎖、重量級鎖 狀態(tài)。

          在學(xué)習(xí)并發(fā)編程知識synchronized時(shí),我們總是難以理解其實(shí)現(xiàn)原理,因?yàn)槠蜴i、輕量級鎖、重量級鎖都涉及到對象頭,所以了解java對象頭是我們深入了解synchronized的前提條件.這篇文章包含了對象頭的解析以及鎖膨脹過程的解析:

          JAVA對象布局之對象頭(Object Header)

          14. 什么是死鎖?什么是活鎖? 什么是線程饑餓?

          JAVA并發(fā)之加鎖導(dǎo)致的活躍性問題剖析

          15.ThreadLocal 是什么?有哪些使用場景?

          1.ThreadLocal  介紹

          2.ThreadLocal  應(yīng)用

          3.ThreadLocal  源碼解析

          3.1解決 Hash 沖突

          4.ThreadLocal 特性

          5..ThreadLocal 內(nèi)存泄露問題

          java并發(fā)之無同步方案-ThreadLocal

          16.說一下synchronized底層實(shí)現(xiàn)原理?

          前置知識,需要了解 對象頭.-->JAVA對象布局之對象頭(Object Header)

          • 同步代碼塊是通過 monitorenter 和 monitorexit 指令獲取線程的執(zhí)行權(quán)

            monitorenter,如果當(dāng)前monitor的進(jìn)入數(shù)為0時(shí),線程就會進(jìn)入monitor,并且把進(jìn)入數(shù)+1,那么該線程就是monitor的擁有者(owner)。

            如果該線程已經(jīng)是monitor的擁有者,又重新進(jìn)入,就會把進(jìn)入數(shù)再次+1。也就是可重入的。

            執(zhí)行monitorexit的線程必須是monitor的擁有者,指令執(zhí)行后,monitor的進(jìn)入數(shù)減1,如果減1后進(jìn)入數(shù)為0,則該線程會退出monitor。其他被阻塞的線程就可以嘗試去獲取monitor的所有權(quán)。

            monitorexit指令出現(xiàn)了兩次,第1次為同步正常退出釋放鎖;第2次為發(fā)生異步退出釋放鎖;

          • 同步方法通過加 ACC_SYNCHRONIZED 標(biāo)識實(shí)現(xiàn)線程的執(zhí)行權(quán)的控制

            標(biāo)志位ACC_SYNCHRONIZED,作用就是一旦執(zhí)行到這個(gè)方法時(shí),就會先判斷是否有標(biāo)志位,如果有這個(gè)標(biāo)志位,就會先嘗試獲取monitor,獲取成功才能執(zhí)行方法,方法執(zhí)行完成后再釋放monitor。在方法執(zhí)行期間,其他線程都無法獲取同一個(gè)monitor。歸根結(jié)底還是對monitor對象的爭奪,只是同步方法是一種隱式的方式來實(shí)現(xiàn)。

          總的來說,synchronized的底層原理是通過monitor對象來完成的

          17.synchronized和volatile的區(qū)別是什么?

          作用:

          • synchronized 表示只有一個(gè)線程可以獲取作用對象的鎖,執(zhí)行代碼,阻塞其他線程。
          • volatile 表示變量在 CPU 的寄存器中是不確定的,必須從主存中讀取。保證多線程環(huán)境下變量的可見性;禁止指令重排序。

          區(qū)別:

          • synchronized 可以作用于變量、方法、對象;volatile 只能作用于變量。
          • synchronized 可以保證線程間的有序性、原子性和可見性;volatile 只保證了可見性和有序性,無法保證原子性。
          • synchronized 線程阻塞,volatile 線程不阻塞。

          18.synchronized和Lock的區(qū)別是什么?

          在多線程情況下,鎖是線程控制的重要途徑。Java為此也提供了2種鎖機(jī)制,synchronized和lock。

          我們這里不討論具體的實(shí)現(xiàn)原理和細(xì)節(jié),只討論它們的區(qū)別

          如果有小伙伴有興趣更深入了解它們,請關(guān)注公眾號:JAVA寶典



          區(qū)別

          • lock是一個(gè)接口,而synchronized是java的一個(gè)關(guān)鍵字。

          • synchronized在發(fā)生異常時(shí)會自動釋放占有的鎖,因此不會出現(xiàn)死鎖;而lock發(fā)生異常時(shí),不會主動釋放占有的鎖,必須手動來釋放鎖,可能引起死鎖的發(fā)生(也稱隱式鎖和顯式鎖)

          • lock等待鎖過程中可以用interrupt來中斷等待,而synchronized只能等待鎖的釋放,不能響應(yīng)中斷;

          • Lock可以通過trylock來知道有沒有獲取鎖,而synchronized不能;

          • Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。(可以通過readwritelock實(shí)現(xiàn)讀寫分離)

          • 在性能上來說,如果競爭資源不激烈,兩者的性能是差不多的,而當(dāng)競爭資源非常激烈時(shí)(即有大量線程同時(shí)競爭),此時(shí)Lock的性能要優(yōu)于synchronized。在使用時(shí)要根據(jù)適當(dāng)情況選擇。

          • synchronized 是 JVM 層面實(shí)現(xiàn)的;Lock 是 JDK 代碼層面實(shí)現(xiàn)

          • synchronized使用Object對象本身的wait 、notify、notifyAll調(diào)度機(jī)制,而Lock可以使用Condition進(jìn)行線程之間的調(diào)度

          • 繼上一條,synchronized只有一個(gè)阻塞隊(duì)列,而Lock使用Condition可以有多個(gè)阻塞隊(duì)列

            synchronized和lock的用法區(qū)別

            • synchronized:在需要同步的對象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括號中表示需要鎖的對象。

            • lock:一般使用ReentrantLock類做為鎖。在加鎖和解鎖處需要通過lock()和unlock()顯示指出。所以一般會在finally塊中寫unlock()以防死鎖。

          19.說一下 你對atomic 的理解 ?

          在JDK5.0之前,想要實(shí)現(xiàn)無鎖無等待的算法是不可能的,除非用本地庫,自從有了Atomic變量類后,這成為可能。

          在java.util.concurrent.atomic包下有這些類:

          • 標(biāo)量類:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
          • 數(shù)組類:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
          • 更新器類:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
          • 復(fù)合變量類:AtomicMarkableReference,AtomicStampedReference

          拿AtomicInteger來舉例,其內(nèi)部實(shí)現(xiàn)不是簡單的使用synchronized,而是一個(gè)更為高效的方式CAS (compare and swap) + volatile和native方法,從而避免了synchronized的高開銷,執(zhí)行效率大為提升。

          /** 
           * Atomically increments by one the current value. 
           * 
           * @return the previous value 
           */
            
          public final int getAndIncrement() {  
              return unsafe.getAndAddInt(this, valueOffset, 1);  

          這里直接調(diào)用一個(gè)叫Unsafe的類去處理,這個(gè)類是用于執(zhí)行低級別、不安全操作的方法集合。盡管這個(gè)類和所有的方法都是公開的(public),但是這個(gè)類的使用仍然受限,你無法在自己的java程序中直接使用該類,因?yàn)橹挥惺谛诺拇a才能獲得該類的實(shí)例。所以我們平時(shí)的代碼是無法使用這個(gè)類的,因?yàn)槠湓O(shè)計(jì)的操作過于偏底層,如若操作不慎可能會帶來很大的災(zāi)難,所以直接禁止普通代碼的訪問,當(dāng)然JDK使用是沒有問題的。

          關(guān)于CAS 在我的另一篇文章: 什么是CAS,ABA問題怎么解決?

          20.說一下 你對Semaphore 的理解 ?

          1. Semaphore就是一個(gè)信號量,它的作用是限制某段代碼塊的并發(fā)數(shù)。
          2. Semaphore有一個(gè)構(gòu)造函數(shù),可以傳入一個(gè)int型整數(shù)n,表示某段代碼最多只有n個(gè)線程可以訪問,
          3. 如果超出了n,那么請等待,等到某個(gè)線程執(zhí)行完畢這段代碼塊,下一個(gè)線程再進(jìn)入。
          4. 由此可以看出如果Semaphore構(gòu)造函數(shù)中傳入的int型整數(shù)n=1,相當(dāng)于變成了一個(gè)synchronized了。

          Semaphore類位于java.util.concurrent包下,它提供了2個(gè)構(gòu)造器:

          //參數(shù)permits表示許可數(shù)目,即同時(shí)可以允許多少線程進(jìn)行訪問  
          public Semaphore(int permits) {  
              sync = new NonfairSync(permits);  
          }  
          //這個(gè)多了一個(gè)參數(shù)fair表示是否是公平的,即等待時(shí)間越久的越先獲取許可  
          public Semaphore(int permits, boolean fair) {  
              sync = (fair)? new FairSync(permits) : new NonfairSync(permits);  
          }  
          • Semaphore類中比較重要的幾個(gè)方法,首先是acquire()、release()方法:
          • acquire()用來獲取一個(gè)許可,若無許可能夠獲得,則會一直等待,直到獲得許可。
          • release()用來釋放許可。注意,在釋放許可之前,必須先獲獲得許可。
          //嘗試獲取一個(gè)許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false  
          public boolean tryAcquire() { };  
          //嘗試獲取一個(gè)許可,若在指定的時(shí)間內(nèi)獲取成功,則立即返回true,否則則立即返回false  
          public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };   
          //嘗試獲取permits個(gè)許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false  
          public boolean tryAcquire(int permits) { };   
          //嘗試獲取permits個(gè)許可,若在指定的時(shí)間內(nèi)獲取成功,則立即返回true  
          public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { };  
          //得到當(dāng)前可用的許可數(shù)目  
          public int availablePermits()



          掃描二維碼

          獲取更多精彩

          JAVA寶典



          喜歡我們就多一個(gè)點(diǎn)贊,多一次分享吧!



          瀏覽 52
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  美女视屏全是a | 亚洲男女操逼 | 天堂8中文在线 | 亚洲AV蜜桃永久无码精品XXX | 日欧美在线视频 |