­

Java執行緒相關知識,執行緒池底層源碼分享和相關面試題(持續更新)

執行緒

 

執行緒有哪些狀態

         */
        NEW, //當執行緒對象對創建後,即進入了新建狀態,如:Thread t = new MyThread()

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE, //當調用執行緒對象的start()方法,執行緒即進入就緒狀態。處於就緒狀態的執行緒,只是說明此執行緒已經做好了準備,隨時等待CPU調度執行,並不是說執行了start()此執行緒立即就會執行

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED, //處於運行狀態中的執行緒由於某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才有機會再次被CPU調用以進入到運行狀態,
            
/** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, //這個狀態下是不能分配CPU執行的,可使用Object.wait(),Thread,join(),LockSupport.park進入這個狀態 /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING,//其實這個狀態和Waiting就是有沒有超時時間的差別,這個狀態下也是不能分配CPU執行的 /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; //在我們的執行緒正常run結束之後或者run一半異常了就是終止狀態

  

終止執行緒的方式

1.Tread.stop(). 2.Tread.interrupt()

推薦使用interrupt,因為使用stop之後,這個執行緒帶有的鎖也會消失,因此不推薦使用這個方法,interrupt()會使得執行緒Waiting和Timed_Waiting狀態的執行緒拋出 interruptedException異常,調用interrupt()方法後,使得Running狀態的執行緒再調用wait()、sleep()、jion()方法時拋出interruptedException異常,需要在catch中處理執行緒異常後的問題。

 

進程和執行緒的區別

來源於一個知乎老哥 //www.zhihu.com/question/25532384 比較容易理解

 

做個簡單的比喻:進程=火車,執行緒=車廂

執行緒在進程下行進(單純的車廂無法運行)

一個進程可以包含多個執行緒(一輛火車可以有多個車廂)

不同進程間數據很難共享(一輛火車上的乘客很難換到另外一輛火車,比如站點換乘)

同一進程下不同執行緒間數據很易共享(A車廂換到B車廂很容易)

進程要比執行緒消耗更多的電腦資源(採用多列火車相比多個車廂更耗資源)

進程間不會相互影響,一個執行緒掛掉將導致整個進程掛掉(一列火車不會影響到另外一列火車,但是如果一列火車上中間的一節車廂著火了,將影響到所有車廂)

進程使用的記憶體地址可以上鎖,即一個執行緒使用某些共享記憶體時,其他執行緒必須等它結束,才能使用這一塊記憶體。(比如火車上的洗手間)-”互斥鎖”

進程使用的記憶體地址可以限定使用量(比如火車上的餐廳,最多只允許多少人進入,如果滿了需要在門口等,等有人出來了才能進去)-「訊號量」

 

sleep方法和wait方法區別

都可以使當前執行緒放棄CPU一定的時間從而暫停,但是如果有鎖的話,sleep方法不會釋放這個鎖,wait方法會, sleep方法必須要設定時間,wait方法可設定可不設定。 sleep屬於Thread類,wait方法屬於Object類

 

wait,notify方法存在的問題(Condition 下的await和signal也是一樣)

notify和wait必須在synchrnizied同步塊中執行,並且成對出現,只有先wait再notify才能被喚醒,否則無法喚醒。

比如notify放在wait前執行,t1先notify,3秒後t2在執行wait方法,程式就一直無法結束。

升級版本:

使用LockSupport的park和unpark

 

實現多執行緒有幾種方式

1.繼承Thread類,重寫run方法

2.實現Runnable介面,重寫run方法

3.實現Callable介面,重寫call方法

4.使用執行緒池

 

ThreadLocal原理

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

  

 

 

進程之間的通訊方式有哪些

管道pipe:管道是一種半雙工的通訊方式,數據只能單向流動,而且只能在具有親緣關係的進程間使用。進程的親緣關係通常是指父子進程關係。

命名管道FIFO:有名管道也是半雙工的通訊方式,但是它允許無親緣關係進程間的通訊。

消息隊列MessageQueue:消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。

共享存儲SharedMemory:共享記憶體就是映射一段能被其他進程所訪問的記憶體,這段共享記憶體由一個進程創建,但多個進程都可以訪問。共享記憶體是最快的 IPC 方式,它是針對其他進程間通訊方式運行效率低而專門設計的。它往往與其他通訊機制,如訊號量,配合使用,來實現進程間的同步和通訊。

訊號量Semaphore:訊號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同執行緒之間的同步手段。

套接字Socket:套解口也是一種進程間通訊機制,與其他通訊機制不同的是,它可用於不同及其間的進程通訊。

訊號 ( sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生。

 

 

 

 

 

 

執行緒池

執行緒池各個參數講解

public ThreadPoolExecutor(int corePoolSize, //執行緒池核心工作執行緒數量,比如newFixedThreadPool中可以自定義的執行緒數量就是這個參數
                              int maximumPoolSize, //執行緒池所有工作執行緒的數量,比如newFixedThreadPool中的最大工作執行緒就是核心執行緒數,newCachedThreadPool中的最大工作執行緒數是Integer.MAX_VALUE
                              long keepAliveTime, //非核心執行緒存活的時間,當核心執行緒不夠用的時候,創建出來的輔助工作執行緒在完成任務空閑多久後會被回收
                              TimeUnit unit, //上面一個參數的單位,分。秒等
                              BlockingQueue<Runnable> workQueue,//底層使用的阻塞隊列數據結構,比如newFixedThreadPool底層使用LinkedBlockingQueue。工作隊列,保存已提交但是未執行的任務
                              ThreadFactory threadFactory, //創建工作執行緒的工廠,保持默認即可
                              RejectedExecutionHandler handler) { //拒絕策略,即在所有工作執行緒數達到上限,底層阻塞隊列也滿了的時候採取的策略
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

常用執行緒池

1.newSingleThreadExecutor 單個執行緒的執行緒池,即執行緒池中每次只有一個執行緒工作,單執行緒串列執行任務

2.newFixedThreadExecutor(n) 固定數量的執行緒池,沒提交一個任務就是一個執行緒,直到達到執行緒池的最大數量,然後後面進入等待隊列直到前面的任務完成才繼續執行

3.newCacheThreadExecutor(推薦使用) 可快取執行緒池,當執行緒池大小超過了處理任務所需的執行緒,那麼就會回收部分空閑(一般是60秒無執行)的執行緒,當有任務來時,又智慧的添加新執行緒來執行。

4.newScheduleThreadExecutor 大小無限制的執行緒池,支援定時和周期性的執行執行緒

執行緒池底層執行原理

 

日常使用中使用哪種執行緒池

上面四種都不使用,我們需要通過ThreadPoolExcutor自定義執行緒池,阿里巴巴java開發手冊明確指出,不允許使用Excutors工具類進行執行緒池的創建,因為某些開發人員不了解底層運行規則,避免資源耗盡的危險

1:FixedThreadPool 和 SingleThreadPool: 允許的請求隊列(底層實現是LinkedBlockingQueue)長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM

2:CachedThreadPool 和 ScheduledThreadPool 允許的創建執行緒數量為Integer.MAX_VALUE,可能會創建大量的執行緒,從而導致OOM

執行緒池4種拒絕策略

new ThreadPoolExecutor.AbortPolicy(); //遇到執行緒池達到最大執行緒數,且隊列已經滿了,直接拋異常 —-默認

new ThreadPoolExecutor.DiscardOldestPolicy() //遇到執行緒池達到最大執行緒數且隊列已經滿了,則丟棄隊列中最前面的任務,

new ThreadPoolExecutor.DiscardPolicy() //遇到執行緒池達到最大執行緒數且隊列已經滿了,丟棄當前任務,不給它加入隊列中

new ThreadPoolExecutor.CallerRunsPolicy();//會將任務返回給提交者進行執行,比如讓main執行緒直接執行run方法

執行緒池優點

1.避免大量執行緒的創建銷毀帶來的性能開銷

2.避免大量執行緒之間因為相互搶佔系統資源導致的阻塞狀態

3.能夠對執行緒進行簡單的管理並提供定時執行,間隔執行等功能

在IO密集型和CPU密集型下執行緒池最大執行緒數選擇

在CPU密集型下,選用cpu核心數+1作為最大執行緒池工作執行緒數量,減少cpu切換任務帶來的開銷

在IO密集型下,儘可能多的加大工作執行緒數量,一般為cpu核心數*2

執行緒池提交任務時excute和submit有什麼區別

1.兩者接收參數不一致,excute接收Runnable介面,submit接收Callable介面

2.submit有返回值,execute沒有返回值

3.submit支援返回Future

執行緒池狀態

private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

RUNNING:執行緒池的初始狀態,可以添加待執行的任務

SHUTDOWN:執行緒池處於待關閉狀態,不接受新任務,但會處理完已接收的任務

STOP:執行緒池立即關閉,不接收新的任務,放棄快取隊列中的任務並且中斷正在處理的任務

TIDYING:執行緒池自主整理狀態,調用 terminated() 方法進行執行緒池整理

TERMINATED:執行緒池終止狀態