天天看點

(一)線程相關面試問題詳解

(一)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 )線程池的執行原理圖:

(一)線程相關面試問題詳解

繼續閱讀