線程池學習筆記

Java線程池ThreadPoolExecutor學習筆記java

1:核心線程:簡單來說就是線程池中可否容許同時併發運行的線程的數量緩存

2:線程池大小:線程池中最多可以容納的線程的數量。併發

3:隊列:對提交過來的任務的處理模式。異步

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

corePoolSize 核心線程數,指保留的線程池大小(不超過maximumPoolSize值時,線程池中最多有corePoolSize 個線程工做)。 
maximumPoolSize 指的是線程池的最大大小(線程池中最大有corePoolSize 個線程可運行)。 
keepAliveTime 指的是空閒線程結束的超時時間(當一個線程不工做時,過keepAliveTime 長時間將中止該線程)。 
unit 是一個枚舉,表示 keepAliveTime 的單位(有NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7個可選值)。 
workQueue 表示存聽任務的隊列(存放須要被線程池執行的線程隊列)。 
handler 拒絕策略(當添加任務數超過maximumPoolSize+workQueue數量時報錯).ide

運行時的分配機制學習

1)當池子大小小於corePoolSize就新建線程,並處理請求測試

2)當池子大小等於corePoolSize,把請求放入workQueue中,池子裏的空閒線程就去從workQueue中取任務並處理this

3)當workQueue放不下新入的任務時,新建線程入池,並處理請求,若是池子大小撐到了maximumPoolSize就用RejectedExecutionHandler來作拒絕處理atom

4)另外,當池子的線程數大於corePoolSize的時候,多餘的線程會等待keepAliveTime長的時間,若是無請求可處理就自行銷燬.net

private ThreadPoolExecutor poolExecutor;
poolExecutor = new ThreadPoolExecutor(3, 10,
                1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(6));

for (int i = 0; i < 30; i++) {
   final int finalI = i;
   Runnable runnable = new Runnable() {
      @Override
      public void run() {
          Log.e("curentThread ","value: " + Thread.currentThread().getName() + "-----" + finalI);
          SystemClock.sleep(3000);
      }
  };
  poolExecutor.execute(runnable);
}

運行時會報錯,由於有30個任務須要運行,而緩存隊列只能緩存6個,剩下的任務須要去建立新線程,但是線程最多建立10因此就會越界報錯

若是new LinkedBlockingDeque<Runnable>()則表示隊列爲無限大,上面運行結果爲

全部的任務都存在隊列中,每次只有三個線程運行,因此顯示thread-1,2,3

poolExecutor = new ThreadPoolExecutor(3, 30,
                1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(6));

若是,將maximumPoolSize改成30,則會出現20多個線程,由於隊列中只存在6個任務,剩下的20多個任務須要去申請新的線程,這個能夠本身運行查看效果

固定數量的線程池newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {

    return new ThreadPoolExecutor(nThreads, nThreads,

                                  0L, TimeUnit.MILLISECONDS,

                                  new LinkedBlockingQueue<Runnable>());

}

這個線程池的核心線程與最大線程爲一個值,不等待,超出核心線程必定時間後的線程就被回收掉了。最多同時運行nThreads數量的線程。

單線程池

public static ExecutorService newSingleThreadExecutor() {

    return new FinalizableDelegatedExecutorService

        (new ThreadPoolExecutor(1, 1,

                                0L, TimeUnit.MILLISECONDS,

                                new LinkedBlockingQueue<Runnable>()));

}

核心線程爲1的固定線程池

 動態線程池(無界線程池)

public static ExecutorService newCachedThreadPool() {

    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                  60L, TimeUnit.SECONDS,

                                  new SynchronousQueue<Runnable>());

}

核心線程池爲0,線程池的最大是無限,等待時間爲60秒,隊列爲直接提交。用的是SynchronousQueue隊列,沒有排隊等待直接添加線程。newCachedThreadPool比較適合沒有固定大小而且比較快速就能完成的小任務,不必維持一個Pool,這比直接new Thread來處理的好處是能在60秒內重用已建立的線程。機制就是查看已經建立的線程若是有空閒的(60秒之內超過就回收了)能夠直接使用,不用從新建立。

線程池雖然隊列能夠換成無窮多個任務,可是當任務過多時就會致使內存溢出,因此當任務很是多時,要使用有界隊列自定義線程池防止oom。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class CustomThreadPoolExecutor {


    private ThreadPoolExecutor pool = null;

    public void init() {
        pool = new ThreadPoolExecutor(
                3,
                10,
                30,
                TimeUnit.MINUTES,
                new ArrayBlockingQueue<Runnable>(5),
                new CustomThreadFactory(),
                new CustomRejectedExecutionHandler());
    }


    public void destory() {
        if(pool != null) {
            pool.shutdownNow();
        }
    }


    public ExecutorService getCustomThreadPoolExecutor() {
        return this.pool;
    }

    private class CustomThreadFactory implements ThreadFactory {

        //原子操做 具體volatile
        private AtomicInteger count = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            String threadName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);
            System.out.println(threadName);
            t.setName(threadName);
            return t;
        }
    }


    private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                // 核心改造點,由blockingqueue的offer會拋棄任務,改爲put阻塞方法,這種不會丟失任務
                executor.getQueue().put(r);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }



    // 測試構造的線程池
    public static void main(String[] args) {

        CustomThreadPoolExecutor exec = new CustomThreadPoolExecutor();
        // 1.初始化
        exec.init();

        ExecutorService pool = exec.getCustomThreadPoolExecutor();
        for(int i=1; i<100; i++) {
            System.out.println("提交第" + i + "個任務!");
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(">>>task is running=====");
                        TimeUnit.SECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

 

LinkedBlockingQueue添加方法put、offer、add介紹http://blog.csdn.net/z69183787/article/details/46986823

AtomicInteger介紹http://haininghacker-foxmail-com.iteye.com/blog/1401346

總結: 
一、用ThreadPoolExecutor自定義線程池,看線程是的用途,若是任務量不大,能夠用無界隊列,若是任務量很是大,要用有界隊列,防止OOM 
二、若是任務量很大,還要求每一個任務都處理成功,要對提交的任務進行阻塞提交,重寫拒絕機制,改成阻塞提交。保證不拋棄一個任務 
三、最大線程數通常設爲2N+1最好,N是CPU核數 
四、核心線程數,看應用,若是是任務,一天跑一次,設置爲0,合適,由於跑完就停掉了,若是是經常使用線程池,看任務量,是保留一個核心仍是幾個核心線程數 
五、若是要獲取任務執行結果,用CompletionService,可是注意,獲取任務的結果的要從新開一個線程獲取,若是在主線程獲取,就要等任務都提交後才獲取,就會阻塞大量任務結果,隊列過大OOM,因此最好異步開個線程獲取結果

參考http://825635381.iteye.com/blog/2184680

相關文章
相關標籤/搜索