1. 程式人生 > >Java基礎學習總結(104)——多執行緒、併發、工具類相關的面試題

Java基礎學習總結(104)——多執行緒、併發、工具類相關的面試題

執行緒的概念
執行緒是程式執行的最小單位,也是作業系統排程和分派CPU的最小單元,是程序中的一個實體,是程序中的實際運作單位。可以在一個程序中啟動多個執行緒來完成不同的任務,這些執行緒共享該程序擁有的資源。
執行緒程序區別
程序是程式的實體,也是執行緒的容器,一個程序可以包含多個執行緒,程序是資源分配的基本單位。
執行緒屬於某個程序,並跟程序中的其他執行緒共享該程序的資源。同一程序中的執行緒可以共享相同的記憶體地址空間,同時每個執行緒還擁有自己單獨的棧記憶體。
在Java中如何實現執行緒
在Java語言層面上只有兩種實現執行緒的方式。繼承java.lang.Thread類和實現java.lang.Runnable介面。java.lang.Thread代表了一個執行緒,而java.lang.Runnable代表了執行緒中執行的任務。
我們應該是使用Runnable還是Thread?
Java不支援多繼承,但允許實現多個介面。所以如果需要繼承其他類,實現Runnable介面是好了。
題外話,Thread表示一個執行緒,每個任務都建立一個執行緒肯定是不妥的,正確的做法應該是初始化一定量的Thread物件,實現Runnable介面建立表示任務的類,並把這些任務對給Thread執行緒執行。
Thread類的start()和run()方法的區別
start()方法會建立新的執行緒並啟動該執行緒,所以該方法會呼叫其他native方法,而run()方法就是正常的Java方法呼叫,即在原來的執行緒中執行java程式碼。
Java中Runnable和Callable的區別
Runnable和Callable都代表要執行緒中執行的任務。Runnable是JDK1.0加入的,而Callable確實是在JDK1.5加入的。
區別:Callable的 call() 方法可以返回值和丟擲異常,
而Runnable的run()方法不能返回值也不能丟擲異常。
Callable是需要使用java.util.concurrent.ExecutorService.submit(Callable<T>)方法提交的,這樣就可以獲得Future物件,該物件可以裝載了Callable介面的call()方法的返回結果。
Java中CyclicBarrier和CountDownLatch的區別
CyclicBarrier和CountDownLatch都可以協同多個執行緒,讓指定數量的執行緒等待其他所有的執行緒都滿足某些條件之後才能繼續執行。CyclicBarrier可以重複使用,而CountdownLatch只能使用一次,如果還需要該能夠,就只能重新new一個CountdownLatch物件。
另外一個是CountdownLatch每次呼叫await()方法之前都需要先呼叫countDown()方法,而CyclicBarrier不需要。
簡述Java記憶體模型
Java記憶體模型包含了一系列的規則和指導原則。
Java語言是跨平臺的,Java的記憶體模型確保了Java在不同的作業系統、CPU、記憶體架構上有確定的行為,特別是在多執行緒的情況下,一個執行緒所做的變動對其他執行緒是否可見是很重要的,這叫做先行發生關係:
程式碼是按照先後順序執行的,就是所謂的程式次序規則。
對同一個鎖的解鎖操作肯定比後續的加鎖操作先發生,也就是所謂的管程鎖定規則。
A write to a volatile field happens-before every subsequent read of that same field, known as Volatile variable rule.
對同一個volatile變數的寫操作肯定比後續的讀操作先發生,也就是所謂的volatile變數規則。
Thread.start()線上程的其他操作發生之前被呼叫,也就是所謂的執行緒啟動規則。
一個執行緒中斷了其他執行緒的操作比被中斷執行緒檢查到中斷先發生,也就是所謂的執行緒中斷規則.
構造物件肯定比該物件被終結先發生,也就是所謂的物件終結規則.
A比B先發生,B比C先發生,那麼A肯定比C先發生,也就是說順序有傳遞性。
Java中的volatile變數有什麼特點
volatile是一個變數修飾符,有且只有成員變數才能用它修飾。多執行緒中如果缺少同步,一個成員變數被修改之後不一定會被其他執行緒可見,如果對該成員變數加上volatile修飾,那麼就可以保證下一次讀操作一定會在上一次寫操作之後發生。
執行緒安全的概念,Vector是否是執行緒安全類?
有多個執行緒同時執行程式碼,如果每次執行的結果都跟單執行緒執行結果一樣,各種變數的值也一直跟預期的一樣,這就可以稱這段程式碼是執行緒安全的,記住是一直,某次跟單執行緒執行結果一樣是不行的。
集合類根據執行緒是否安全可以分為兩類,執行緒安全和非執行緒安全。Vector是使用同步方法來實現執行緒安全的,在JDK1.1的時候引入的 而和它類似的的ArrayList是在JDK1.2引入的,並且不是執行緒安全的。
競態條件的概念
多個執行緒對共享的資料同時進行讀寫的時候,最終的結果取決於這些執行緒的執行順序,而如果執行順序不正確就會出現錯誤結果,這就是所謂的靜態條件。
Java停止執行緒的方法
首先必須要明確Java本身沒有提供停止執行緒的方法。特別值得一提的是JDK1.0中的一些控制方法,比如stop(), suspend() 和 resume()等,由於存在潛在的死鎖問題因此在後續的版本中被棄用了。當run() 或call() 方法執行完執行緒就會自動結束,如果確實需要手動結束某個執行緒,可以使用一些變數來標誌是否退出run()或call(),如果沒有采用同步機制,則需要使用volatile修飾,還可以通過取消任務來中斷執行緒,即通過判斷是否中斷。
執行緒發生異常的結果
如果異常沒有被捕獲,則該執行緒會終止執行。
注意:
Thread靜態方法
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
和例項方法
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)
設定了可以抓取執行緒沒有被捕獲的異常,但是執行緒還是會終執行的。
如何線上程之間共享資料
共享物件(類的靜態變數或類的例項變數)
也可以使用阻塞佇列等併發的集合。
notify和notifyAll的區別
notify()方法只能隨機喚醒某個正在等待的執行緒;
notifyAll()可以喚醒所有正在等待的執行緒,變成等待該物件上的鎖,如果該物件已經被解鎖,它們就會去一起去競爭該物件的鎖。notifyAll()能夠確保至少有一個執行緒能繼續執行。
注意:
當有多個執行緒在等待的時候,必須線上程被喚醒並執行完操作之後繼續呼叫notify()或notifyAll(),以便喚醒其他正在等待的執行緒。
wait,notify和notifyAll是Object類的方法的原因
Java提供的是物件級的鎖而不是執行緒級的鎖,這樣每個物件都有鎖。呼叫物件中的wait()方法意思就是在等待該物件的鎖,而不是某個執行緒的鎖,如果wait()方法定義在Thread類中,就等於只有執行緒才能有鎖,而其他任何物件不可以有鎖。
wait,notify、notifyAll都是鎖級別的操作,把他們定義在Object類中是因為鎖是屬於物件的。
ThreadLocal變數含義(執行緒區域性變數)
ThreadLocal的物件即使被多個執行緒共享,每個執行緒設定進去的值都不會相互干擾,而且自己只能訪問到自己設定的值,這樣相同的物件相同的方法呼叫卻各不一樣,就好像每個執行緒在ThreadLoad上都擁有自己獨立的一個變數
為了保證執行緒安全,某些變數不應該被多個執行緒共享;但是如果這些變數(比如SimpleDateFormat)的建立是非常昂貴的,那麼就應該整個執行緒都共享該變數,這時候有什麼辦法能夠做到讓某個非靜態變數可以被某個執行緒的所有方法都訪問到呢?很難,什麼辦法都很難做到。而使用ThreadLocal就可以做到。
首先,通過整個執行緒複用某些變數減少了代價昂貴的物件的建立。
其次,沒有使用高代價的同步或不變性就獲得了執行緒安全。這裡需要解釋一下,如果某個物件(比如電話簿,有增刪功能)一定需要被所有的物件共享,那麼為了執行緒安全肯定必須使用同步機制。但是如果只是需要在某個執行緒內共享,那麼怎麼辦到?沒有辦法,只能使用靜態變數,可是靜態變數不可以一個執行緒一個,因為我們不知道會有多少個執行緒,所以只能採取一個靜態變數被所有的執行緒共享,這就需要同步。
JDK中有ThreadLocalRandom類,它減少了在多執行緒環境中建立代價高昂的Random物件的個數。