多線程編程(六)-Executor與ThreadPoolExecutor的使用


  • 使用Executors工廠類創建線程池

    1、使用newCachedThreadPool()方法創建無界線程池

      newCachedThreadPool()方法創建的是無界線程池,可以進行線程自動回收,此類線程池中存放線程個數理論值為Integer.MAX_VALUE最大值。

package com.wjg.unit4_2_2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Run {
    public static void main(String[] args) throws InterruptedException {
        Run run = new Run();
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            executorService.execute(run.new MyRunnable(" "+(i+1)));
        }
        Thread.sleep(3000);
        System.out.println();
        System.out.println();
        for (int i = 0; i < 5; i++) {
            executorService.execute(run.new MyRunnable(" "+(i+1)));
        }
    }
    
    
    public class MyRunnable implements Runnable{
        private String username;
        
        public MyRunnable(String username) {
            this.username = username;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
            System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
        }
        
    }
}

執行結果:

pool-1-thread-1 username= 1 begin 1488268086641

pool-1-thread-3 username= 3 begin 1488268086641

pool-1-thread-2 username= 2 begin 1488268086641

pool-1-thread-2 username= 2 end   1488268086641

pool-1-thread-4 username= 4 begin 1488268086642

pool-1-thread-4 username= 4 end   1488268086642

pool-1-thread-3 username= 3 end   1488268086641

pool-1-thread-1 username= 1 end   1488268086641

pool-1-thread-5 username= 5 begin 1488268086642

pool-1-thread-5 username= 5 end   1488268086642

 

 

pool-1-thread-5 username= 1 begin 1488268089647

pool-1-thread-3 username= 3 begin 1488268089648

pool-1-thread-4 username= 4 begin 1488268089648

pool-1-thread-1 username= 2 begin 1488268089647

pool-1-thread-1 username= 2 end   1488268089648

pool-1-thread-4 username= 4 end   1488268089648

pool-1-thread-3 username= 3 end   1488268089648

pool-1-thread-2 username= 5 begin 1488268089648

pool-1-thread-2 username= 5 end   1488268089648

pool-1-thread-5 username= 1 end   1488268089648

 

通過線程的名字,可以看出來線程是從池中取出來的,是可以復用的。

    2、使用newCachedThreadPool(ThreadFactory)定制線程工廠

      構造函數ThreadFactory是實現定制Thread的作用,具體可以看下面的例子。

 

package com.wjg.unit4_2_3;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class Run {
    public static void main(String[] args) {
        Run run = new Run();
        MyThreadFactory factory = run.new MyThreadFactory();
        ExecutorService executorService = Executors.newCachedThreadPool(factory);
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                System.out.println("當前線程的自定義名稱為:"+ Thread.currentThread().getName());
            }
        });
    }
    
    
    public class MyThreadFactory implements ThreadFactory{

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("自定義名稱:"+new Date());
            return thread;
        }
        
    }
}

 

執行結果:

當前線程的自定義名稱為:自定義名稱:Tue Feb 28 15:58:13 CST 2017

 

    3、使用newFixedThreadPool(int) 方法創建有界線程池

      此方法創建的是有界線程池,也就是池中的線程的個數可以指定最大值。

 

package com.wjg.unit4_2_4;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Run {
    public static void main(String[] args) {
        Run run = new Run();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            executorService.execute(run.new MyRunnable(" "+(i+1)));
        }
    }
    
    public class MyRunnable implements Runnable{
        private String username;
        
        public MyRunnable(String username) {
            this.username = username;
        }


        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
        }
        
    }
}

執行結果:

pool-1-thread-1 username= 1 begin 1488269132995

pool-1-thread-3 username= 3 begin 1488269132995

pool-1-thread-2 username= 2 begin 1488269132995

pool-1-thread-2 username= 2 end   1488269136000

pool-1-thread-3 username= 3 end   1488269136000

pool-1-thread-2 username= 4 begin 1488269136000

pool-1-thread-3 username= 5 begin 1488269136000

pool-1-thread-1 username= 1 end   1488269136000

pool-1-thread-2 username= 4 end   1488269139002

pool-1-thread-3 username= 5 end   1488269139002

通過執行結果可以看出,線程池中的線程最大數量為3。

 

    4、使用newSingleThreadExecutor()方法創建單一線程池

      此方法可以創建單一線程池,線程池里只有一個線程,單一線程池可以實現以隊列的方式來執行任務。

 

package com.wjg.unit4_2_5;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Run {
    public static void main(String[] args) {
        Run run = new Run();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            executorService.execute(run.new MyRunnable(" "+(i+1)));
        }
    }
    
    
    public class MyRunnable implements Runnable{
        private String username;
        
        public MyRunnable(String username) {
            this.username = username;
        }


        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
        }
        
    }
}

 

 

執行結果:

pool-1-thread-1 username= 1 begin 1488269392403

pool-1-thread-1 username= 1 end   1488269395409

pool-1-thread-1 username= 2 begin 1488269395409

pool-1-thread-1 username= 2 end   1488269398412

pool-1-thread-1 username= 3 begin 1488269398413

pool-1-thread-1 username= 3 end   1488269401418

pool-1-thread-1 username= 4 begin 1488269401418

pool-1-thread-1 username= 4 end   1488269404422

pool-1-thread-1 username= 5 begin 1488269404422

pool-1-thread-1 username= 5 end   1488269407423

 

由執行結果的線程名字可以看出,線程池中只有一個線程。

 

  • ThreadPoolExecutor的使用

    類ThreadPoolExecutor可以非常方便的創建線程池對象。

    常用的構造方法有ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)

    參數解釋如下:

    corePoolSize:池中所保存的線程數,包括空閑線程數,也就是核心池的大小。

    maximumPoolSize:池中允許的最大線程數

    keepAliveTime:當線程數量大於corePoolSize值時,在沒有超過指定的時間內是不從線程池中將空閑線程刪除的,如果超過此時間,則刪除。

    unit:keepAliveTime參數的時間單位。

    workQueue:執行前用於保持任務的隊列。此隊列僅保存由execute方法提交的Runnable任務。

    為了更好地理解這些參數在使用上的一些關系,可以將它們進行詳細化的注釋:

    (1)A代表execute(runnable)欲執行的runnable的數量;

    (2)B代表corePoolSize;

    (3)C代表maximumPoolSize;

    (4)D代表A-B(假設A>=B);

    (5)E代表newLinkedBlockingDeque<Runnable>()隊列,無構造函數。

    (6)F代表SynchronousQueue隊列;

    (7)G代表keepAliveTime;

    在使用線程池的過程下,會出現以下的集中情況:

    (1)如果A<=B,那么馬上創建線程運行這個任務,並不放入擴展隊列Queue中,其他參數功能忽略;

    (2)如果A>B&&A<=C&&E,則C和G參數忽略,並把D放入E中等待被執行;

    (3)如果A>B&&A<=C&&F,則C和G參數有效,並且馬上創建線程運行這些任務,而不把D放入F中,D執行完任務后在指定時間后發生超時時將D進行清除。

    (4)如果A>B&&A>C&&E,則C和G參數忽略,並把D放入E中等待被執行;

    (5)如果A>B&&A>C&&F,則處理C的任務,其他任務則不再處理拋出異常;

 

    方法getActiveCount()的作用是取得有多少個線程正在執行任務。

    方法getPoolSize()的作用是獲得當前線程池里面有多少個線程,這些線程數包括正在執行任務的線程,也包括正在休眠的線程。

    方法getCompletedTaskCount()的作用是取得有多少個線程已經執行完任務了。

    方法getCorePoolSize()的作用是取得構造方法傳入的corePoolSize參數值。

    方法getMaximumPoolSize()的作用是取得構造方法傳入的maximumPoolSize的值。

    方法getTaskCount()的作用是取得有多少個任務發送給了線程池。

    方法shutdown()的作用是使當前未執行完的線程繼續執行,而不再添加新的任務task,該方法不會阻塞,調用之后,主線程main馬上結束,而線程池會繼續運行直到所有任務執行完才會停止。

    方法shutdownNow()的作用是中斷所有的任務task,並且拋出InterruptedException異常,前提是在Runnable中使用if(Thread.currentThread().isInterrupted()==true)語句來判斷當前線程的中斷狀態,而未執行的線程不再執行,也就是從執行隊列中清除。如果不手工加if語句及拋出異常,則池中正在運行的線程知道執行完畢,而未執行的線程不再執行,也從執行隊列中清除。

    方法isShutDown()的作用是判斷線程池是否已經關閉。  

    方法isTerminating()的作用是判斷線程池是否正在關閉中。

    方法isTerminated()的作用是判斷線程池是否已經關閉。

    方法awaitTermination(long timeout,TimeUnit unit)的作用是查看在指定的時間之內,線程池是否已經終止工作,也就是最多等待多少時間后去判斷線程池是否已經終止工作。

    方法allowsCoreThreadTimeOut(boolean) 的作用是配置核心線程是否有超時的效果。

    方法prestartCoreThread()的作用是每調用一次就創建一個核心線程,返回值為boolean。

    方法prestartAllCoreThreads()的作用是啟動全部核心線程,返回值是啟動核心線程的數量。

  • 線程池ThreadPoolExecutor的拒絕策略

    線程池中的資源全部被占用的時候,對新添加的Task任務有不同的處理策略,在默認的情況ThreadPoolExecutor類中有4個不同的處理方式:

    (1)AbortPolicy:當任務添加到線程池中被拒絕時,它將拋出RejectedExecutionException異常。

    (2)CallerRunsPolicy:當任務添加到線程池被拒絕時,會使用調用線程池的Thread線程對象處理被拒絕的任務。

    (3)DiscardOldestPolicy:當任務添加到線程池被拒絕時,線程池會放棄等待隊列中最舊的未處理任務,然后將拒絕的任務添加到等待隊列中。

    (4)DiscardPolicy:當任務添加到線程池中被拒絕時,線程池將丟棄被拒絕的任務。


免責聲明!

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



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