java 多線程

3.1.1、線程安全

​ 多個線程訪問某個類時,不管運行環境和調度方式,這個類始終表現出正確的行爲,稱爲線程安全 。

3.1.2、多線程編程3個核心概念

  1. 原子性:同數據庫,要麼全執行,要麼全失敗。(鎖和同步)

  2. 可見性:併發訪問共享變量時,對共享變量的修改,其他都能看到。(volatile修飾)

  3. 順序性:jvm優化,調整順序代碼,程序的執行順序並非按照代碼的先後順序。

3.1.3、線程之間的狀態

 

1、新建狀態(New):新創建了一個線程對象。

2、就緒狀態(Runnable):線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取CPU的使用權。

3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。

4、阻塞狀態(Blocked):阻塞狀態是線程因爲某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。阻塞的情況分三種:

(一)、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。(wait會釋放持有的鎖)

(二)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池中。

(三)、其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。(注意,sleep是不會釋放持有的鎖)

5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

3.1.3.2、線程阻塞情況

線程阻塞 描述
1.等待阻塞 運行的線程執行wait()方法,會釋佔有的資源,jvm會將線程放入“等待池中”。不能自動喚醒,必須依靠其他線程調用notify()/notifyall()
2.同步阻塞 運行的線程在獲取對象的同步鎖時,若該同步鎖被其他線程佔用,jvm會把該線程放入“鎖池中”
3.其他阻塞 運行的線程執行sleep()/join()方法,或者發出IO請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時,join()等待線程終止或者超時,或者IO處理完畢時,線程重新轉入就緒狀態。

 

3.1.4、java四種線程池簡介

四種線程池內部構造都是來自同一個方法:

public ThreadPoolExecutor(int corePoolSize,  //核心線程數
                              int maximumPoolSize,//最大線程數
                              long keepAliveTime,//存活時間
                              TimeUnit unit,//時間單位
                              BlockingQueue<Runnable> workQueue,//任務隊列
                              ThreadFactory threadFactory) { //拒絕策略
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

BlockingQueue: 任務隊列,如果當前線程池中核心線程數達到了corePoolSize時,且當前所有線程都屬於活動狀態時,則將新的任務添加到該隊列中。基本上有以下幾個實現: 1) ArrayBlockQueue:基於數組結構的有界隊列,此隊列按FIFO(first in first out)原則對任務進行排序。如果隊列已滿,新的任務將會被採取拒絕策略對待。 2)LinkedBlockingQueue: 基於鏈表的無界隊列,按FIFO原則排序。因爲是無界的,所以不存在滿的情況,此時拒絕策略無效 3) PriorityBlockingQueue:具有優先級的隊列的有界隊列,可以自定義優先級,默認爲自然排序。

Handler:拒絕策略,當線程池和workQueue都滿了的情況下,對新任務採取的處理策略,有四種默認實現: 1) AbortPolicy:拒絕任務,且還拋出RejectedExecutionException異常,線程池默認策略 2) CallerRunPolicy:拒絕新任務進入,如果該線程池還沒有被關閉,那麼這個新的任務在執行線程中被調用 3) DiscardOldestPolicy: 如果執行程序尚未關閉,則位於頭部的任務將會被移除,然後重試執行任務(再次失敗,則重複該過程),這樣將會導致新的任務將會被執行,而先前的任務將會被移除。 4)DiscardPolicy:沒有添加進去的任務將會被拋棄,也不拋出異常。基本上爲靜默模式。

3.1.4.1、fixThreadPool  正規線程

​ 它是一種線程數量固定的線程池,當線程處於空閒狀態時,他們並不會被回收,除非線程池被關閉。當所有的線程都處於活動狀態時,新的任務都會處於等待狀態,直到有線程空閒出來。FixedThreadPool只有核心線程,且該核心線程都不會被回收,這意味着它可以更快地響應外界的請求。jdk實現如下:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
​

3.1.4.2、caCheThreadPool  緩存線程池

​ 只有非核心線程,最大線程數很大(Int.Max(values)),它會爲每一個任務添加一個新的線程,這邊有一個超時機制,當空閒的線程超過60s內沒有用到的話,就會被回收。缺點就是沒有考慮到系統的實際內存大小。

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

3.1.4.3、singleThreadPoll   單線程線程池

​ 看這個名字就知道這個傢伙是隻有一個核心線程,就是一個孤家寡人,通過指定的順序將任務一個個丟到線程,都乖乖的排隊等待執行,不處理併發的操作,不會被回收。缺點就是一個人幹活效率慢。

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
​

3.1.4.4、ScheduledThreadPoll

  這個線程池就厲害了,是唯一一個有延遲執行和週期重複執行的線程池。它的核心線程池固定,非核心線程的數量沒有限制,但是閒置時會立即會被回收 。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

注意:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章