老漢談——線程池原理

線程池

JDK1.8官方介紹:

Thread pools address two different problems: they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks.

線程池解決了兩個問題:它們通常在執行大量異步任務時提供改進性能問題,由於減少了每個任務的資源開銷,他們提供邊界和管理資源的手段,包括線程,消費在執行任務的集合。每個ThreadPoolExecutor 維持一些基本的統計數據,比如完成任務數量。

線程池的原理

  • 線程池本身是不創建和啓動線程的,只有任務到達時纔會,即當一個任務提交到線程池時,就會創建一個線程。但可以重寫動態使用方法prestartCoreThread()或prestartCoreAllThread()。
  • 當線程數沒有達到核心線程數corePoolSize時,就會創建一個新的線程,不需要排隊。當線程數大於核心線程數corePoolSize時,就會放到workerQueue任務隊列裏等待。
  • 當核心線程數小於等於線程總數maximunPoolSize時,如果隊列中有等待的線程,則線程池會用空餘的線程來執行隊列的線程;否則,線程池就會採用拒絕策略來終止新的任務。
  • 如果某個線程會話時間超出保持會話時間keepAliveTime時,就會終止當前線程任務,然後加入新的線程任務。可以使用setKeepAliveTime(long, java.util.concurrent.TimeUnit)動態改變參數。一般來說,這個活動策略是已經超出了corePoolSize的線程時才應用。但只要keepAliveTime非0時,就可以通過allowCoreThreadTimeOut(true)方法,使超時策略應用在覈心線程中。

任務排隊

-直接提交:工作隊列WorkQueue默認是SychronousQueue。它繼承了AbstractQueue,實現了BlockingQueue。他的處理任務容量是無邊界的。
當運行的線程數大於或等於核心線程corePoolSize時,纔會加入隊列,但直接提交是同步阻塞的。從下圖的工作原理可以看出,直接提交maximunPoolSize最好無限大即無容量大小。

/**

- Creates a {@code SynchronousQueue} with nonfair access policy. 
  */ 
  public SynchronousQueue() { 
  this(false); 
  }

/**

- Creates a {@code SynchronousQueue} with the specified fairness policy. 
  *
- @param fair if true, waiting threads contend in FIFO order for
- access; otherwise the order is unspecified. 
  */ 
  public SynchronousQueue(boolean fair) { //fair 公平性標識 
  // 公平性TrasferQueue、 非公平行TransferStack 
  transferer = fair ? new TransferQueue() : new TransferStack(); 
  }

線程池類:ThreadPoolExecutor
應用場景:交換任工作,生產者的線程消費和消費者的線程同步傳遞消息、時間或任務。簡單的說,任務2必須依賴於任務1的信息,
這裏寫圖片描述
-無界隊列:LinkedBlockingQueue。它保證所有的超出corePoolSize的線程都放入隊列裏。這樣創建的線程不會超出corePoolSize.,因此maximumPoolSize值就無效了。
線程池類:newFixedThreadPool

- Creates a {@code LinkedBlockingQueue} with a capacity of
- {@link Integer#MAX_VALUE}. 
  */ 
  public LinkedBlockingQueue() { 
  this(Integer.MAX_VALUE); 
  }

/**
- Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity. 
  *
- @param capacity the capacity of this queue
- @throws IllegalArgumentException if {@code capacity} is not greater
- than zero 
  */ 
  public LinkedBlockingQueue(int capacity) { 
  if (capacity <= 0) throw new IllegalArgumentException(); 
  this.capacity = capacity; 
  last = head = new Node(null); 
  }

/**
- Creates a {@code LinkedBlockingQueue} with a capacity of
- {@link Integer#MAX_VALUE}, initially containing the elements of the
- given collection,
- added in traversal order of the collection's iterator. 
  *
- @param c the collection of elements to initially contain
- @throws NullPointerException if the specified collection or any
- of its elements are null 
  */ 
  public LinkedBlockingQueue(Collection c) { 
  this(Integer.MAX_VALUE); 
  final ReentrantLock putLock = this.putLock; 
  putLock.lock(); // Never contended, but necessary for visibility 
  try { 
  int n = 0; 
  for (E e : c) { 
  if (e == null) 
  throw new NullPointerException(); 
  if (n == capacity) 
  throw new IllegalStateException("Queue full"); 
  enqueue(new Node(e)); 
  ++n; 
  } 
  count.set(n); 
  } finally { 
  putLock.unlock(); 
  } 
  }

應用場景:適合任務之間互不影響
這裏寫圖片描述
-有界隊列:ArrayBlockingQueue。這是最複雜的一種。不推薦使用。當使用有限的MaximumPoolSize時,有界隊列可以防止資源耗盡,但可能很難控制與調整。隊列的容量是有限的。
一般使用大池小列或小池大列,前者會增加CPU使用率,但遇到不可接受的調度開銷,也會降低吞吐量;後者會減少CPU使用率、操作系統資源和上下文的切換,但可能導致人工來降低吞吐量。

/**

- Creates an {@code ArrayBlockingQueue} with the given (fixed)
- capacity and default access policy. 
  *
- @param capacity the capacity of this queue
- @throws IllegalArgumentException if {@code capacity < 1} 
  */ 
  public ArrayBlockingQueue(int capacity) { 
  this(capacity, false); 
  }

/**

- Creates an {@code ArrayBlockingQueue} with the given (fixed)
- capacity and the specified access policy. 
  *
- @param capacity the capacity of this queue
- @param fair if {@code true} then queue accesses for threads blocked
- on insertion or removal, are processed in FIFO order;
- if {@code false} the access order is unspecified.
- @throws IllegalArgumentException if {@code capacity < 1} 
  */ 
  public ArrayBlockingQueue(int capacity, boolean fair) { 
  if (capacity <= 0) 
  throw new IllegalArgumentException(); 
  this.items = new Object[capacity]; 
  lock = new ReentrantLock(fair); 
  notEmpty = lock.newCondition(); 
  notFull = lock.newCondition(); 
  }

public ArrayBlockingQueue(int capacity, boolean fair, 
Collection c) { 
this(capacity, fair);

final ReentrantLock lock = this.lock;
lock.lock(); // Lock only for visibility, not mutual exclusion
try {
    int i = 0;
    try {
        for (E e : c) {
            checkNotNull(e);
            items[i++] = e;
        }
    } catch (ArrayIndexOutOfBoundsException ex) {
        throw new IllegalArgumentException();
    }
    count = i;
    putIndex = (i == capacity) ? 0 : i;
} finally {
    lock.unlock();
}

}

從上可以看出:有界隊列ArrayBlockingQueue和無界隊列LinkedBlockingQueue都是使用ReentrantLock重入鎖,而同步隊列sychoronousQueue是使用公平性TransferQueue和非共性TransferStack,即轉移數據隊列和轉移數據棧,從數據結構便知數據的順序。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章