初識Java多線程

今天開始來學習一下有關Java多線程的知識,主要有如下知識點:android

  • 進程與線程
  • 線程的生命週期
  • 中斷線程
  • 線程池

進程與線程

什麼是進程?

進程就是在運行過程當中的程序,就好像手機運行中的微信,QQ,這些就叫作進程。緩存

什麼是線程?

線程就是進程的執行單元,就好像一個音樂軟件能夠聽音樂,下載音樂,這些任務都是由線程來完成的。微信

進程與線程的關係

一個進程能夠擁有多個線程,一個線程必需要有一個父進程。
線程之間共享父進程的共享資源,相互之間協同完成進程所要完成的任務。
一個線程能夠建立和撤銷另外一個線程,同一個進程的多個線程之間能夠併發執行。多線程

線程的生命週期


clipboard.png

  • 新建(New)

當線程實例被new出來以後,調用start()方法以前,線程處於新建狀態。併發

  • 可運行(Runnable)

當線程實例調用start()方法以後,線程調度器分配處理器資源以前,線程處於可運行狀態
或者線程調度器分配處理器資源給線程以後,線程處於運行中狀態,這兩種狀況都屬於可運行狀態。性能

  • 等待(Waitting)

當線程處於運行狀態時,線程執行了obj.wait()或Thread.join()方法、LockSupport.park()
以及Thread.sleep()時,線程處於等待狀態。學習

  • 超時等待(Timed Waitting)

當線程處於運行狀態時,線程執行了obj.wait(long)、 Thread.join(long)、LockSupport.parkNanos、
LockSupport.parkUntil以及Thread.sleep(long)方法時,線程處於超時等待狀態。this

  • 阻塞(Blocked)

當線程處於運行狀態時,獲取鎖失敗,線程進入等待隊列,同時狀態變爲阻塞。spa

  • 終止(Terminated)

當線程執行完畢或出現異常提早結束時,線程進入終止狀態線程

注意:在任何給定時刻,一個可運行的線程可能正在運行也可能沒有運行(這就是爲何將這個狀態稱爲可運行而不是運行)。

中斷線程

interrupt方法能夠用來請求終止線程。
當對一個線程調用interrupt方法時,線程的中斷狀態將被置位爲true。這是每個線程都具備的boolean標誌。
每一個線程都應該不時地檢查這個標誌,已判斷線程是否被中斷。

源碼解析

public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();//檢查權限
        
    synchronized (blockerLock) {
        //判斷線程是否被阻塞
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();
            b.interrupt(this);//若是是阻塞線程,則把中斷狀態設爲false後返回
            return;
        }
    }
    interrupt0();//不然把中斷狀態設爲true
}

若是線程被阻塞,就沒法檢測中斷狀態。
當在一個被阻塞的線程(調用sleep或wait)上調用interrupt方法時,阻塞調用將會被Interrupt Exception異常中斷。
若是阻塞線程調用了interrupt()方法,那麼會拋出異常,設置標誌位爲false,同時該線程會退出阻塞的。(利用這個特性能夠打破死鎖)

interrupted和isInterrupted區別:

  • interrupted方法是一個靜態方法,它檢測當前的線程是否被中斷。調用interrupted方法會清除該線程的中斷狀態。
  • isInterrupted方法是一個實例方法,可用來檢測是否有線程被中斷。調用這個方法不會改變中斷狀態。

線程池

概述

當須要執行的任務增多時,單個線程是知足不了需求的,此時就須要建立多個線程來完成須要。
多線程的最大好處就在於提升CPU的利用率和提升執行效率,同時也存在着一些弊端:頻繁的建立和銷燬線程會產生不少的性能開銷。
爲了解決這個問題,線程池孕育而生。

  • 複用線程池中的線程,減小建立和銷燬線程的性能開銷
  • 控制線程的併發數,避免對資源競爭而致使阻塞現象
  • 更好地管理線。

ExecutorService

在Java中,線程池的代碼起源之Executor(翻譯過來就是執行者)注意:這是一個接口。
Executor有一個ExecutorService子接口實際上,通常說線程池接口,基本上說的是這個ExecutorService。
ExecutorService接口的默認實現類爲ThreadPoolExecutor(翻譯過來就是線程池執行者)。

ThreadPoolExecutor

翻譯過來就是線程池執行器,它是線程池的真正實現,構造方法提供了一些參數來配置線程池。

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

參數詳解

corePoolSize:線程池中核心的線程數。

核心線程默認狀況下會一直存活在線程池中,即便這個核心線程啥也不幹(閒置狀態)。
若是指定ThreadPoolExecutor的allowCoreThreadTimeOut這個屬性爲true,那麼核心線程若是不幹活(閒置狀態)的話,超過必定時間( keepAliveTime),就會被銷燬掉。

maximumPollize:線程池所能容納的最大線程數。超過限制,新線程會被阻塞。

keepAliveTime:一個非核心線程,若是不幹活(閒置狀態)的時長,超過這個參數所設定的時長,就會被銷燬掉。可是,若是設置了allowCoreThreadTimeOut = true,則會做用於核心線程。

unit:超時等待時間單位。

workQueue:線程池中的任務隊列。每次執行execute()會把runnable對象存儲在這個隊列中。若是隊列滿了,則新建非核心線程執行任務。

threadFactory:線程工廠,爲線程池提供建立新線程的功能。

執行任務

ThreadPoolExecutor poolExecutor;
//初始化一個線程池
poolExecutor = new ThreadPoolExecutor(corePoolSize: 3,
                                      maximumPoolSize: 5,
                                      keepAliveTime: 30,
                                      TimeUnit.SECONDS,
                                      new ArrayBlockingQueue<Runnable>( capacity: 2));
//向線程池中添加任務
poolExecutor.execute(new Runnable() {
      public void run() {
});

首先咱們初始化一個線程池後,便可調用execute這個方法,裏面傳入Runnable便可向線程池添加任務。

問題又來了,既然線程池新添加了任務,那麼線程池是如何處理這些批量任務?

  • 若是線程數量未達到corePoolSize,則新建一個核心線程執行任務。
  • 若是線程數量達到了corePoolSize,則將任務移入隊列等待執行。
  • 若是隊列已滿,新建線程(非核心線程)執行任務。
  • 若是隊列已滿,總線程數又達到了maximumPoolSize,就會由RejectedExecutionHandler拋出異常。

經常使用的線程池

Executors提供了建立經常使用線程池的靜態方法,接下也會大概講解一下經常使用的四種線程池:

  • 定長線程池(FixedThreadPool)
  • 單線程化線程池(SingleThreadExecutor)
  • 定時線程池(ScheduledThreadPool)
  • 緩存線程池(CachedThreadPool)

FixedThreadPool

建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。

public static ExecutorService newFixedThreadPool(int nThreads){
   return new ThreadPoolExecutor(nThreads, nThreads
                                keepAliveTime: OL, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>());
}

特色:

  • 線程數量固定
  • 只有核心線程,而且不會被回收
  • 超過corePoolSize的線程,他們會在隊列中等待,直到有一個線程可用。
  • 適用於控制線程的最大併發數

CachedThreadPool

建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。

public static ExecutorService newCachedThreadPool() {
   return new ThneadPoolExecutor(corePoolSize: 0, maximumPoolSize: Integer.MAX VALUE,
                                 keepAliveTime: 60L, TimeUnit. SECONDS,
                                 new SynchronousQueue<Runnable>();
}

特色:

  • 無核心線程
  • 非核心線程數量無限制
  • 對於空閒線程回收靈活//超過60s沒有使用則進行回收
  • 適用於大量且耗時少的任務

ScheduledThreadPool

建立一個定長任務線程池,支持定時及週期性任務執行。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
 }

特色:

  • 線程數量固定
  • 非核心數量無限制
  • 適用於定時或者週期性任務

SingleThreadExecutor

建立一個單線程的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。

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

特色:

  • 只有一個核心線程
  • 任務隊列無限制
  • 不須要考慮線程同步問題
  • 適用於一些由於併發而致使問題的操做
總結

有關多線程暫且說到這裏,先對多線程有個初步的認識,後面會深刻研究多線程。

參考:Java線程池

相關文章
相關標籤/搜索