亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java多線程面試題(面試官常問)

 更新時(shí)間:2021年03月30日 14:14:35   作者:唔仄lo咚鏘  
這篇文章主要介紹了Java多線程面試題(面試官常問),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

進(jìn)程和線程

進(jìn)程是程序的一次執(zhí)行過程,是系統(tǒng)運(yùn)行程序的基本單位,因此進(jìn)程是動(dòng)態(tài)的。系統(tǒng)運(yùn)行一個(gè)程序即是從一個(gè)進(jìn)程從創(chuàng)建、運(yùn)行到消亡的過程。在Java中,當(dāng)我們啟動(dòng)main函數(shù)時(shí)其實(shí)就是啟動(dòng)了一個(gè)JVM的進(jìn)程,而mian函數(shù)所在的線程就是這個(gè)進(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è)線程之間切換工作是,負(fù)擔(dān)要比進(jìn)程小很多,所以線程也稱輕量級(jí)進(jìn)程。

并發(fā)和并行

  • 并發(fā):同一時(shí)間段內(nèi),多個(gè)任務(wù)都在執(zhí)行(單位時(shí)間內(nèi)不一定同時(shí)執(zhí)行)
  • 并行:單位時(shí)間內(nèi),多個(gè)任務(wù)同時(shí)執(zhí)行。

上下文切換

多線程編程中一般線程的個(gè)數(shù)都大于CPU核心的個(gè)數(shù),而一個(gè)CPU核心在任意時(shí)刻內(nèi)只能被一個(gè)線程使用,為了讓這些線程都能得到有效執(zhí)行,CPU采取的策略時(shí)為每個(gè)線程分配時(shí)間片并輪轉(zhuǎn)的形式。當(dāng)一個(gè)線程的時(shí)間片用完的時(shí)候就會(huì)重新處于就緒狀態(tài)讓給其他線程使用,這個(gè)過程屬于一次上下文切換。

換句話說,當(dāng)前任務(wù)在執(zhí)行完CPU時(shí)間片切換到另一個(gè)任務(wù)之前會(huì)先保存自己的狀態(tài),以便下次再切換會(huì)這個(gè)任務(wù)時(shí),可以再加載這個(gè)任務(wù)的狀態(tài)。任務(wù)從保存到再加載的過程就是一次上下文切換。

sleep()和wait()

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

 start()和run()

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

當(dāng)我們new一個(gè)Thread時(shí),線程進(jìn)入了新建狀態(tài),調(diào)用start()方法,會(huì)啟動(dòng)一個(gè)線程并使線程進(jìn)入就緒狀態(tài),等分到時(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í)行,并不是在某個(gè)線程中執(zhí)行它,所以這不是多線程工作。

synchronized關(guān)鍵字

synchronized關(guān)鍵字是解決多個(gè)線程之間訪問資源的同步性,可以保證被它修飾的方法或代碼塊在任意時(shí)刻只能有一個(gè)線程執(zhí)行。

synchronized主要的三種使用方式:

1.修飾實(shí)例方法

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

2.修飾靜態(tài)方法

給當(dāng)前類加鎖,會(huì)作用于類的所有對(duì)象實(shí)例,因?yàn)殪o態(tài)成員是類成員,不屬于任何一個(gè)實(shí)例對(duì)象,所以線程A調(diào)用一個(gè)實(shí)例對(duì)象的非靜態(tài)synchronized方法,而線程B調(diào)用該實(shí)例對(duì)象所屬類的靜態(tài)synchronized方法時(shí)是允許的,不會(huì)沖突互斥。因?yàn)樵L問靜態(tài)synchronized方法占用的是當(dāng)前類的鎖,而訪問非靜態(tài)synchronized方法占用的是當(dāng)前實(shí)例對(duì)象鎖。

3.修飾代碼塊

指定加鎖對(duì)象,進(jìn)入同步代碼庫前要獲得給定對(duì)象的鎖。

synchronized和ReentrantLock:

1.兩者都是可重入鎖
即自己可以再次獲取自己的內(nèi)部鎖。比如一個(gè)線程獲得了某個(gè)對(duì)象的鎖,此時(shí)這個(gè)對(duì)象鎖還沒有釋放,當(dāng)其再次想要獲取這個(gè)對(duì)象的鎖時(shí)還是可以獲取的。

2.前者依賴JVM而后者依賴API
synchronized是依賴于JVM實(shí)現(xiàn)的,ReentrantLock是依賴于JDK層面實(shí)現(xiàn)的。

3.ReentrantLock比synchronized功能多
ReentrantLock增加了一些高級(jí)功能,主要說有三點(diǎn):①等待可中斷②可實(shí)現(xiàn)公平鎖③可實(shí)現(xiàn)選擇性通知。

volatile關(guān)鍵字

當(dāng)前Java內(nèi)存模型下,線程可以把變量保存到本地內(nèi)存(如寄存器)中,而不是直接在主存中進(jìn)行讀寫。這就可能造成一個(gè)線程在主存中修改了一個(gè)變量的值,而另外一個(gè)線程還在繼續(xù)使用它在寄存器中變量值的拷貝,造成數(shù)據(jù)的不一致。

volatile關(guān)鍵字就是解決這個(gè)問題,指示JVM這個(gè)變量不穩(wěn)定,每次使用它都要到主存中進(jìn)行讀取。除此之外還有一個(gè)重要的作用是防重排。

并發(fā)執(zhí)行的三個(gè)重要特性:

1.原子性

要么所有的操作都得到執(zhí)行并且不會(huì)收到任何因素干擾而中斷,要么所有的操作都不執(zhí)行。可使用synchronized來保證代碼原子性。

2.可見性
當(dāng)對(duì)一個(gè)共享變量進(jìn)行了修改后,那么另外的線程都是立即可以看到修改后的最新值。volatile可以保證可見性。

3.有序性
代碼在執(zhí)行過程中的先后順序,Java在編譯器以及運(yùn)行期間的優(yōu)化,代碼的執(zhí)行順序未必就是編寫代碼時(shí)候的順序,即指令重排。volatile可以禁止指令重排優(yōu)化。

ThreadLocal

通常情況下,我們創(chuàng)建的變量時(shí)可以被任何一個(gè)線程訪問并修改的。如果要實(shí)現(xiàn)每一個(gè)線程都有自己的專屬本地變量該如何解決?這就需要ThreadLocal類了。

ThreadLocal類主要解決的就是讓每個(gè)線程綁定自己的值,可以將ThreadLocal類比喻成存放數(shù)據(jù)的盒子,盒子中可以存儲(chǔ)每個(gè)線程的私有數(shù)據(jù)。

當(dāng)創(chuàng)建一個(gè)ThreadLocal變量后,訪問這個(gè)變量的每個(gè)線程都會(huì)有這個(gè)變量的本地副本,這也是ThreadLocal名稱的由來??梢允褂胓et()和set()方法來獲取默認(rèn)值或?qū)⑵渲蹈臑楫?dāng)前線程所存副本的值,從而避免了線程安全問題。

實(shí)際上,ThreadLocal類有一個(gè)靜態(tài)內(nèi)部類ThreadLocalMap,可以把ThreadLocalMap看作是ThreadLocal類定制的HashMap,最終的變量是放在了當(dāng)前線程的ThreadLocalMap中,而不是ThreadLocal類上,可以看作ThreadLocal類是ThreadLocalMap的封裝,傳遞了值。

如果再同一個(gè)線程中聲明了兩個(gè)ThradLocal對(duì)象的話,會(huì)使用Thread內(nèi)部僅有的那個(gè)ThreadLocalMap存放數(shù)據(jù)的,TheadLocalMap的key就是ThreadLocal對(duì)象,value就是ThreadLocal對(duì)象調(diào)用set方法設(shè)置的值。

ThreadLocalMap使用的key為ThreadLocal的弱引用,而value是強(qiáng)引用。所以ThreadLocal沒有被外部強(qiáng)引用的情況下,在垃圾回收的時(shí)候,key 會(huì)被清理掉,而value不會(huì)被
清理掉。這樣一來,ThreadLocalMap中就會(huì)出現(xiàn)key為null的Entry。假如我們不做任何措施的話,value永遠(yuǎn)無法被GC回收,這個(gè)時(shí)候就可能會(huì)產(chǎn)生內(nèi)存泄露。ThreadLocalMap實(shí)現(xiàn)中已經(jīng)考慮了這種情況,在調(diào)用set()、get()、remove()方法的時(shí)會(huì)清理掉key為null 的記錄。使用完ThreadLocal方法后最好手動(dòng)調(diào)用remove()方法。

插播反爬信息 )博主CSDN地址:https://wzlodq.blog.csdn.net/

線程池

池化技術(shù)大家應(yīng)該很熟悉,線程池、數(shù)據(jù)庫連接池、Http連接池等等都是對(duì)這思想的應(yīng)用。池化技術(shù)的思想主要是為了減少每次獲取資源的消耗,提高對(duì)資源的利用率。
使用線程池,可以降低資源消耗、提高響應(yīng)速度、提高線程的可管理性。

Runnable和Callable

Runnable接口不會(huì)返回結(jié)果或拋出檢查異常,但Callable接口可以。
工具類Excutors可以實(shí)現(xiàn)Runnable對(duì)對(duì)象和Callable對(duì)象之間的相互轉(zhuǎn)換。

@FunctionalInterface
public interface Runnable{
 //沒有返回值也無法拋出異常
 public abstract void run();
}
@FunctionalInterface
public interface Callable<V>{
 //@return 計(jì)算得出結(jié)果
 //@throws 如果無法計(jì)算結(jié)果,則拋出異常
}

execute()和submit()

  1. execute()方法用于提交不需要返回值的任務(wù),所以無法判斷任務(wù)是否被線程池執(zhí)行成功與否。
  2. submit()方法用于提交需要返回值的任務(wù)。線程池會(huì)返回一個(gè)Future類型對(duì)象,通過這個(gè)對(duì)象可以判斷任務(wù)是否執(zhí)行成功。

 創(chuàng)建線程池

  1. 通過構(gòu)造器ThreadpoolExecutor實(shí)現(xiàn)(下面介紹)。
  2. 通過工具類Executors實(shí)現(xiàn)(不推薦)

ThreadPoolExecutor: .

  • FixedThreadPool:該訪法返回一個(gè)固定線程數(shù)量的線程池。該線程池中的線程數(shù)量始終不變。當(dāng)有一個(gè)新的任務(wù)提交時(shí),線程池中若有空閑線程,則立即執(zhí)行。若沒有,則新的任務(wù)會(huì)被暫存在一個(gè)任務(wù)隊(duì)列中,待有線程空閑時(shí),便處理在任務(wù)隊(duì)列中的任務(wù)。
  • SingleThreadExecutor:方法返回- 個(gè)只有一一個(gè)線程的線程池。若多余一個(gè)任務(wù)被提交到該線程池,任務(wù)會(huì)被保存在一個(gè)任務(wù)隊(duì)列中,待線程空閑,按先入先出的順序執(zhí)行隊(duì)列中的任務(wù)。
  • CachedThreadPool:該方法返回一個(gè)可 根據(jù)實(shí)際情況調(diào)整線程數(shù)量的線程池。線程池的線程數(shù)量不確定,但若有空閑線程可以復(fù)用,則會(huì)優(yōu)先使用可復(fù)用的線程。若所有線程均在工作,又有新的任務(wù)提交,則會(huì)創(chuàng)建新的線程處理任務(wù)。所有線程在當(dāng)前任務(wù)執(zhí)行完畢后,將返回線程池進(jìn)行復(fù)用。

ThreadPoolExecutor

  • ThreadPoolExecutor構(gòu)造函數(shù)重要參數(shù)分析:
  • corePoolSize:核⼼線程數(shù)線程數(shù)定義了最⼩可以同時(shí)運(yùn)⾏的線程數(shù)量。
  • maximumPoolSize:當(dāng)隊(duì)列中存放的任務(wù)達(dá)到隊(duì)列容量的時(shí)候,當(dāng)前可以同時(shí)運(yùn)⾏的線程數(shù)量變?yōu)樽?#12068;線程數(shù)。
  • workQueue:當(dāng)新任務(wù)來的時(shí)候會(huì)先判斷當(dāng)前運(yùn)⾏的線程數(shù)量是否達(dá)到核⼼線程數(shù),如果達(dá)到的話,新任務(wù)就會(huì)被存放在隊(duì)列中。
  • keepAliveTime:當(dāng)線程池中的線程數(shù)量⼤于 corePoolSize 的時(shí)候,如果這時(shí)沒有新的任務(wù)提交,核⼼線程外的線程不會(huì)⽴即銷毀,⽽是會(huì)等待,直到等待的時(shí)間超過了keepAliveTime 才會(huì)被回收銷毀;
  • unit:keepAliveTime 參數(shù)的時(shí)間單位。
  • threadFactory:executor 創(chuàng)建新線程的時(shí)候會(huì)⽤到。
  • handler:飽和策略

①ThreadPoolExecutor.AbortPolicy:拋出 RejectedExecutionException 來拒絕新任務(wù)的處理。
②ThreadPoolExecutor.CallerRunsPolicy:調(diào)⽤執(zhí)⾏⾃⼰的線程運(yùn)⾏任務(wù)。會(huì)降低對(duì)于新任務(wù)提交速度,影響程序的整體性能,另外會(huì)增加隊(duì)列容量。
③ThreadPoolExecutor.DiscardPolicy:不處理新任務(wù),直接丟棄掉。
④ThreadPoolExecutor.DiscardOldestPolicy:此策略將丟棄最早的未處理的任務(wù)請(qǐng)求。

Demo

模擬了 10 個(gè)任務(wù),我們配置的核⼼線程數(shù)為 5 、等待隊(duì)列容量為 100 ,所以每次只能存在5個(gè)任務(wù)同時(shí)執(zhí)⾏,剩下的5個(gè)任務(wù)會(huì)被放到等待隊(duì)列中去。當(dāng)前的 5 個(gè)任務(wù)之⾏完成后,才會(huì)之⾏剩下的 5 個(gè)任務(wù)。

public class MyRunnable implements Runnable {
 private String command;
 public MyRunnable(String s) {
  this.command = s;
 }
 @Override
 public void run() {
  System.out.println(Thread.currentThread().getName() + "開始時(shí)間:" + new Date());
    processCommand();
  System.out.println(Thread.currentThread().getName() + "結(jié)束時(shí)間:" + new Date());
 }
 private void processCommand() {
  try {
   Thread.sleep(3000); //設(shè)花費(fèi)3秒執(zhí)行任務(wù)
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
 @Override
 public String toString() {
  return this.command;
 }
}
public class Demo {
 private static final int CORE_POOL_SIZE = 5;//核⼼線程數(shù)為 5
 private static final int MAX_POOL_SIZE = 10;//最⼤線程數(shù) 10
 private static final int QUEUE_CAPACITY = 100;//容量100
 private static final Long KEEP_ALIVE_TIME = 1L;//等待時(shí)間為 1L
 public static void main(String[] args) {
  //通過ThreadPoolExecutor構(gòu)造函數(shù)⾃定義參數(shù)創(chuàng)建
  ThreadPoolExecutor executor = new ThreadPoolExecutor(
    CORE_POOL_SIZE,
    MAX_POOL_SIZE,
    KEEP_ALIVE_TIME,
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(QUEUE_CAPACITY),
    new ThreadPoolExecutor.CallerRunsPolicy());//飽和策略
  for (int i = 0; i < 10; i++) {
   //創(chuàng)建WorkerThread對(duì)象(WorkerThread類實(shí)現(xiàn)了Runnable接⼝)
   Runnable worker = new MyRunnable("" + i);
   executor.execute(worker);//執(zhí)⾏Runnable
  }
  executor.shutdown();//終⽌線程池
  while (!executor.isTerminated()) {
  }
  System.out.println("結(jié)束");
 }
}
/*運(yùn)行結(jié)果如下:
pool-1-thread-3開始時(shí)間:Mon Mar 29 22:46:02 CST 2021
pool-1-thread-2開始時(shí)間:Mon Mar 29 22:46:02 CST 2021
pool-1-thread-4開始時(shí)間:Mon Mar 29 22:46:02 CST 2021
pool-1-thread-5開始時(shí)間:Mon Mar 29 22:46:02 CST 2021
pool-1-thread-1開始時(shí)間:Mon Mar 29 22:46:02 CST 2021
pool-1-thread-2結(jié)束時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-2開始時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-3結(jié)束時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-3開始時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-4結(jié)束時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-4開始時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-5結(jié)束時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-5開始時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-1結(jié)束時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-1開始時(shí)間:Mon Mar 29 22:46:07 CST 2021
pool-1-thread-2結(jié)束時(shí)間:Mon Mar 29 22:46:12 CST 2021
pool-1-thread-3結(jié)束時(shí)間:Mon Mar 29 22:46:12 CST 2021
pool-1-thread-4結(jié)束時(shí)間:Mon Mar 29 22:46:12 CST 2021
pool-1-thread-5結(jié)束時(shí)間:Mon Mar 29 22:46:12 CST 2021
pool-1-thread-1結(jié)束時(shí)間:Mon Mar 29 22:46:12 CST 2021
結(jié)束
*/

到此這篇關(guān)于Java多線程面試題(面試官常問)的文章就介紹到這了,更多相關(guān)Java多線程面試題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論