(一)Runnable接口和Callable接口的差別?
1.相同點:①都是一種類型的接口(廢話)②都可應用于Executors
2.不同點:①Callable接口的call()方法,Runnable實作run()方法。
②call()可以有傳回值(),run()方法沒有傳回值。
③call()方法可以抛出checked exception異常,run()方法不能抛出異常【隻能内部消化】。
④Runnable接口JDK1.1就有了,Callble在JDK1.5才有。
(二)wait()和sleep()方法的差別?
1.差別:
①:sleep()屬于Thread類的靜态方法。 wait()屬于Object類的方法。
②【鎖】:sleep()睡着了,-->仍保持對象鎖。【時間到了,自動喚醒過來】
wait()等待時,會釋放對象鎖。【沒設定時間-->就需要其他線程調用notify/notifyAll喚醒】
Thread.sleep(0)的作用是:“觸發 作業系統 立刻 重新進行一次CPU競争”。
共同打斷:通過interrupt()方法打斷線程暫停狀态-->會立刻抛出InterrruptedException(不建議這麼用)。
③:使用範圍:wait,notify 和 notifyAll隻能在同步控制塊兒裡面使用,而sleep可以在任何地方使用。
synchronized(x){
x.notify()
//或者wait()
}
④:當兩者都定義在同步中時: 線程執行到sleep()不會釋放鎖。線程執行到wait(),會釋放鎖。
(三).synchronized、Lock、ReentrantLock、ReadWriteLock。
(1.0)synchronized:修飾方法或代碼塊兒【常用的同步機制】-->盡量隻鎖 少量 共享的資料塊兒。
(2.0)Lock【是個接口】:常用的實作類 有ReentrantLock、ReadWriteLock(ReentrantReadWriteLock)。
1.0:ReentrantLock【可序列化;分為公平鎖 和 不公平鎖(預設)】:是一個可重入的互斥鎖Lock。
【常用方法,以及應用場景:①tryLock(),②tryLock(5,TimeUnit.SECONDS),③lock(),④lockInterruptibly()-->詳情請見部落格 ReentrantLock常用方法-場景 詳解】
它的lock機制有2種-->忽略中斷鎖 和 響應中斷鎖
(①)公平鎖:如果有另一個線程持有鎖或者有其他線程在等待隊列中等待這個鎖,那麼新發出的請求的線程将被放入到隊列中。【類似于 會排隊】。
(②)非公平鎖:隻有當鎖被某個線程持有時,新送出請求的線程才會被放入隊列中【類似于 不排隊】 (③)非公平鎖性能高于公平鎖性能的原因:
在恢複一個被挂起的線程與該線程真正運作之間存在着嚴重的延遲。非公平鎖 會充分利用這段時間。并提高吞吐量。
eg:假設線程A持有一個鎖,并且線程B請求這個鎖。由于鎖被A持有,是以B将被挂起。當A釋放鎖時,B将被喚醒,是以B會再次嘗試擷取這個鎖。與此同時,如果線程C也請求這個鎖,那麼C很可能會在B被完全喚醒之前獲得、使用以及釋放這個鎖。這樣就是一種雙赢的局面:B獲得鎖的時刻并沒有推遲,C更早的獲得了鎖,并且吞吐量也提高了。
【注意】當持有鎖的時間相對較長 或者 請求鎖的平均時間間隔較長,應該使用公平鎖。在這些情況下,插隊帶來的吞吐量提升(當鎖處于可用狀态時,線程卻還處于被喚醒的過程中)可能不會出現。
(四)什麼是ThreadLocal?【實作 線程局部變量】
(1.0)是JDK 1.2版本推出的 解決多線程并發的一種新思路。---> JDK 1.5 ThreadLocal<>;
當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,是以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
(2.0)ThreadLoacl類的常用方法:
①:protested T initialValue():-->傳回此線程 局部變量 的目前線程的 “初始值”。
②:void set(T value)-->将此線程局部變量的目前線程副本中的值設定為指定值。
③:T get():-->傳回此線程局部變量的目前線程的值。
④:void remove()--->移除此線程 局部變量目前線程的值。
(3.0)ThreadLocal中的變量副本如何實作的?
ThreadLocal是如何做到為每一個線程維護變量的副本的呢?其實實作的思路很簡單:在ThreadLocal類中有一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵 為線程對象,而值 對應線程的變量副本。
(4.0)代碼操作:
public class TestThreadLocal {
//通過匿名内部類 覆寫ThreadLocal的initialValue()方法,指定初始值
private static ThreadLocal<Integer> seqNum=new ThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};
//擷取下一個序列值
public int getNextNum(){
seqNum.set(seqNum.get()+1);
return seqNum.get();
}
public static void main(String[] args) {
TestThreadLocal ttl=new TestThreadLocal();
//開啟三個分線程
TestClient t1=new TestClient(ttl);
TestClient t2=new TestClient(ttl);
TestClient t3=new TestClient(ttl);
t1.start();
t2.start();
t3.start();
}
private static class TestClient extends Thread{
private TestThreadLocal testThreadLocal;
public TestClient(TestThreadLocal t){
this.testThreadLocal=t;
}
public void run(){
for (int i=0;i<3;++i){
//每個線程列印三次
System.out.println("Thread["+Thread.currentThread().getName()+"]----》testThreadLocal" +
"["+testThreadLocal.getNextNum()+"]");
}
}
}
}
結果顯示:

結果分析:各自都列印三次值一 一對應變化;線程變量 之間 互不影響,互相獨立。
(四)建立 線程池 的4種方式。
(1.0) java通過Executors提供了四種線程池:
①:newCachedThreadPool 建立一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程。
②:newFixedThreadPool 建立一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。
③:newScheduledThreadPool 建立一個定長線程池,支援定時及周期性任務執行。
④: newSingleThreadExecutor 建立一個單線程化的線程池,它隻會用唯一的工作線程來執行任務,保證所有任務 按照指定順序(FIFO, LIFO, 優先級)執行。
(2.0)題外話
newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool,這三者都直接或間接調用了ThreadPoolExecutor,為什麼它們三者沒有直接是其子類,而是通過Executors來執行個體化呢?這是所采用的靜态工廠方法,在java.util.Connections接口中同樣也是采用的靜态工廠方法來建立相關的類。這樣有很多好處,靜态工廠方法是用來産生對象的,産生什麼對象沒關系,隻要傳回原傳回類型或原傳回類型的子類型都可以,降低API數目和使用難度,在《Effective Java》中的第1條就是靜态工廠方法。
(五)ThreadPoolExecutor的内部工作原理。
(1.0)JDK1.7 構造函數
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
① corePoolSize:核心線程池的線程數量
②maximumPoolSize:最大的線程池線程數量
③keepAliveTime:線程活動保持時間,線程池的工作線程空閑後,保持存活的時間。
④unit:線程活動保持時間的機關。
⑤ workQueue:指定任務隊列所使用的阻塞隊列
(2.0 )線程池的執行原理圖: