Java必會之多線程


一、線程的基本知識

1.1 線程知識

進程和線程的關系和區別

線程:

線程是進程的基本執行單元,進程想要執行任務,必須要有線程。程序啟動默認開啟一條線程,這個線程被稱為主線程。

進程:

進程是指在系統中正在運行的一個應用程序。每個進程之間是獨立的,每個進程均運行在其專用且受保護的內存里。

線程的六個狀態:

NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED

Thread流程圖:

Thread的方法:

方法 說明
void join() t.join() 當前線程調用其他線程的t.join()方法,當前線程進入等待狀態,當前線程不會釋放已經持有的鎖。線程t執行完畢后,當前線程進入就緒狀態。
static native void sleep() 靜態方法,線程睡眠,並讓出CPU時間片
void wait() 當前線程調用對象的wait()方法,當前線程釋放對象鎖,進入等待隊列。依靠notify()/notifyAll()喚醒。
native void notify() 喚醒在此對象監視器上等待的單個線程,選擇是任意性的。
native void notifyAll() 發送信號通知所有等待線程

1.2 線程安全

並發的相關性質:

  • 原子性:原子操作。對基本數據類型的讀取和賦值操作是原子性操作,即這些操作是不可被中斷的,要么執行,要么不執行。

  • 可見性:對於可見性,java提供了volatile關鍵字來保證可見性。

    當一個共享變量被volatile修飾時,他會保證修改的值會立即被更新到主存,當有其他線程需要讀取時,他會去主存中讀取新值。

    volatile 不保證原子性。

  • 有序性:Java允許編譯器和處理器對指令重排序,但是重排序不會影響到單線程的執行,卻會影響到多線程並發執行的正確性。

synchronized

使用對象頭標記字實現

使用場景:

  • 修飾方法:
    一個對象中的加鎖方法只允許一個線程訪問。
  • 修飾靜態方法:
    由於靜態方法是類方法,所以多個線程不同對象訪問這個靜態方法,也是可以保證同步的。
  • 修飾代碼塊:
    如:synchronized(obj){...} 這里的obj可以是類中的一個屬性,也可以是對象,這時他跟修飾普通方法一樣,如果obj是Object.class這樣的,那么效果跟修飾靜態方法類似。

volatile

  1. 每次讀取都強制從主內存刷數據
  2. 適用場景:單個線程寫,多個線程讀
  3. 原則:能不用就不用,不確定的時候也不用
  4. 語義
  • 可見性
  • 禁止指令重排序(不完全保證有序性)
  • 不能保證原子性。

為什么不保證有序性呢?舉個例子說明

上述代碼,語句1和2,不會被重排到3的后面,4和5也不會到3的前面。但是1和2的順序、4和5的順序無法保證。

final

final定義類型 說明
final class XXX 不允許繼承
final 方法 不允許Override
final 局部變量 不允許修改
final 實例屬性 構造函數、初始化塊后不能變更。只能賦值一次。構造函數結束返回時,final域最新的值保證對其他線程可見。
final static 屬性 靜態塊執行后不允許變更,只能賦值一次

二、線程池

  1. Executor :執行者 -頂層接口
  2. ExecutorService :繼承於Executor,線程池的接口API
  3. ThreadFactory:線程工廠
  4. Executors:工具類

submit方法和execute方法的區別:

  • submit方法:有返回值,用Future封裝,執行的方法異常了可以在主線程里catch到。
  • execute方法:無返回值,方法執行異常是捕捉不到的

如下圖:

ExecutorService主要方法:

構造線程池的參數

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize :核心線程數
maximumPoolSize:最大線程數
keepAliveTime:當線程數超過了核心線程數,空閑線程需要等待多久等待不到新任務就終止。
workQueue:任務隊列
threadFactory:線程工廠
handler:拒絕策略

提交任務邏輯:

  1. 判斷核心線程數
  2. 加入workQueue
  3. 判斷最大線程數,沒達到就創建
  4. 執行拒絕策略(默認是拋異常)

緩沖隊列

  1. ArrayBlockingQueue:規定大小的BlockingQueue,構造時必須指定大小
  2. LinkedBlockingQueue:大小不固定的BlockingQueue,如果構造時指定大小,則有大小限制,不指定大小,則用Integer.MAX_VALUE來決定
  3. PriorityBlockingQueue:類似於LinkedBlockingQueue,不同在它根據對象的自然順序或者構造函數的Comparator進行排序,不是FIFO
  4. SynchronizedQueue:特殊的BlockingQueue,對其的操作必須是放和取交替完成。

拒絕策略:

  1. AbortPolicy:丟棄任務並拋異常
  2. DiscardPolicy:丟棄任務,不拋異常
  3. DiscardOldestPolicy:丟棄隊列最前面的任務,重新提交被拒絕的任務
  4. CallerRunsPolicy:由提交任務的線程處理該任務。

線程工廠(ThreadFactory):

自定義示例:

public class CustomThreadFactory implements ThreadFactory {

    private AtomicInteger count=new AtomicInteger();

    @Override
    public Thread newThread(Runnable r) {
        Thread thread=new Thread(r);
        thread.setDaemon(false);
        thread.setName("customThread-"+count.getAndIncrement());
        return thread;
    }
}

線程工具類:

  1. newSingleThreadExecutor

    創建一個單線程的線程池。如果這個線程因為異常結束,那么會有一個新的線程替代它。此線程池保證所有的任務的執行順序按任務提交順序執行。

  2. newFixedThreadPool

    創建固定大小的線程池。缺點:隊列使用的LinkedBlockingQueue,且沒有限制大小。

  3. newCachedThreadPool

    創建一個可緩存的隊列,如果線程池大小超過了處理任務需要的線程,那么就會回收部分空閑線程。缺點:此線程池不會對線程池大小做限制。

  4. newScheduledThreadPool

    創建一個大小無限的線程池。此線程池支持定時以及周期性執行任務的需求。

創建固定線程池的經驗:

假設服務器核心數為N

  1. 如果是CPU密集型應用,則線程池大小設置為N或N+1
  2. 如果是IO密集型應用,則線程池大小設置為2N或2N+2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM