1. 程式人生 > >ThreadPoolExecutor 的三種提交任務方式

ThreadPoolExecutor 的三種提交任務方式

添加 正在 一次 數據 copy stack 多線程 ont ole

學習內容:

ExecutorService線程池的應用...

1.如何創建線程池...

2.調用線程池的方法,獲取線程執行完畢後的結果...

3.關閉線程...

首先我們先了解一下到底什麽是線程池,只有了解了其中的道理,我們才能夠進行應用...java.util.concurrent.ExecutorService表述了異步執行的機制

首先我們簡單的舉一個例子...

技術分享圖片
package executor;

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

public class Executor {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        System.out.println("cc");
        ExecutorService executorService=Executors.newFixedThreadPool(10);
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    
                    System.out.println("aa");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
        System.out.println("bb");
    }

}
技術分享圖片

這裏我們指定了十個線程處於一個線程池內部,線程池的原理其實就是對多線程的一個管理,為了實現異步機制的一種方法,其實就是多個線程執行多個任務,最終這些線程通過線程池進行管理...不用手動去維護...一次可以處理多個任務,這樣就可以迅速的進行相應...比如說一個網站成為了熱點網站,那麽對於大量的點擊量,就必須要對每一次的點擊做出迅速的處理,這樣才能達到更好的交互效果...這樣就需要多個線程去處理這些請求,以便能夠更好的提供服務...

1. 簡單的說一下如何創建線程池進行初始化....創建線程有幾種常用方式...這裏都是使用了Executors工廠來實例化對象,同時我們也可以根據需求自己去寫一個ExecutorService...這幾種常用的方法有一定的區別...

ExecutorService executorService1 = Executors.newSingleThreadExecutor();

ExecutorService executorService2 = Executors.newFixedThreadPool(10);

ExecutorService executorService3 = Executors.newScheduledThreadPool(10);
ExecutorService executorService4 = Executors.newCacheThreadPool();
Executors.newSingleThreadExecutor()
單例線程,表示在任意的時間段內,線程池中只有一個線程在工作...
Executors.newCacheThreadPool()
緩存線程池,先查看線程池中是否有當前執行線程的緩存,如果有就resue(復用),如果沒有,那麽需要創建一個線程來完成當前的調用.並且這類線程池只能完成一些生存期很短的一些任務.並且這類線程池內部規定能resue(復用)的線程,空閑的時間不能超過60s,一旦超過了60s,就會被移出線程池.
Executors.newFixedThreadPool(10) 固定型線程池,和newCacheThreadPool()差不多,也能夠實現resue(復用),但是這個池子規定了線程的最大數量,也就是說當池子有空閑時,那麽新的任務將會在空閑線程中被執行,一旦線程池內的線程都在進行工作,那麽新的任務就必須等待線程池有空閑的時候才能夠進入線程池,其他的任務繼續排隊等待.這類池子沒有規定其空閑的時間到底有多長.這一類的池子更適用於服務器.
Executors.newScheduledThreadPool(10)

調度型線程池,調度型線程池會根據Scheduled(任務列表)進行延遲執行,或者是進行周期性的執行.適用於一些周期性的工作.

這就是線程池創建的幾種方式...我們需要根據不同的需求來適當的選擇到底使用哪種線程池...

2.那麽創建了線程池以後就需要對線程池進行調用..將任務加載到其中...

i.ExecutorService.execute(Runnable);

第一種調用方式...通過這種方式將線程任務加載到線程池當中...我們可以添加多個任務...貼上一個完整的代碼...大家看一下代碼的解釋就明白到底是怎麽回事了..不難理解...

技術分享圖片
package executor;

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

public class Executor {

    /**
     * @param args
     * 
     */
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ExecutorService executorService=Executors.newFixedThreadPool(2);//定義了線程池中最大存在的線程數目...
        
        //添加了第一個任務...這個任務會一直被執行...
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    
                    System.out.println("aa");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
        
        //添加第二個任務,被執行三次停止...
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                int i=0;
                while(true){
                    i++;
                    System.out.println("bb");
                    if(i==3){
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }    
                }
            }
        });
        
        /*
         * @param
         * 第三個任務...只有當第二個任務被執行三次之後才能被執行...
         * 由於三次前,線程池已經滿了,這個任務是輪不到被執行的..只能排隊進行等待. 
         * 三次之後,第二個任務被終止,也就是線程池中出現了空閑的狀態,所以這個任務將被放入到線程池中執行...
         * */
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    
                    System.out.println("cc");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
    }

}
技術分享圖片

ii.executorService.submit(Runnable) 第二種調用方式...這種方式與第一種的區別在於可以使用一個Future對象來判斷當前的線程是否執行完畢...但是這種方法只能判斷當前的線程是否執行完畢,無法返回數據信息...

技術分享圖片
Future future = executorService.submit(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});
//如果任務結束執行則返回 null
System.out.println("future.get()=" + future.get());
技術分享圖片

iii.executorService.submit(Callable)... 第三種調用方式...這種調用方式與前一種有所不同,傳遞的參數為Callable對象,Callable與Runnbale很相似,但是Callable的call()方法可以返回數據信息...通過Future就能夠獲取到其中的信息..而Runnbale.run()方法時無法獲取數據信息的....Future應用於多線程...可以獲取call()方法返回的數據信息...其實他是一種模式,是為了性能優化而提供的一種思想...這裏我就不說Future...

技術分享圖片
uture future = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("Asynchronous Callable");
        return "Callable Result";
    }
});

System.out.println("future.get() = " + future.get());

//上述樣例代碼會輸出如下結果: 
//Asynchronous Callable
//future.get() = Callable Result
技術分享圖片

iv.inVokeAny()...第四種調用方式...方法 invokeAny() 接收一個包含 Callable 對象的集合作為參數。調用該方法不會返回 Future 對象,而是返回集合中某一個 Callable 對象的結果,而且無法保證調用之後返回的結果是哪一個Callable,只知道它是這些 Callable 中一個執行結束的 Callable 對象...說實話這個方法我不知道它創建的目的到底是什麽...這裏執行後的結果是隨機的...也就是輸出是不固定的....

技術分享圖片
ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 1";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 2";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 3";
    }
});

String result = executorService.invokeAny(callables);

System.out.println("result = " + result);
技術分享圖片

v.inVokeAll()這個方法和上面不同的地方就在於它可以返回所有Callable的執行結果...獲取到所有的執行結果,我們可以對其進行管理...相對而言,我覺得這個方法比上一個更實用吧...

技術分享圖片
ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 1";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 2";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 3";
    }
});

List<Future<String>> futures = executorService.invokeAll(callables);

for(Future<String> future : futures){
    System.out.println("future.get = " + future.get());
技術分享圖片

3.線程池的關閉...

當我們不需要使用線程池的時候,我們需要對其進行關閉...有兩種方法可以關閉掉線程池...

i.shutdown()...

shutdown並不是直接關閉線程池,而是不再接受新的任務...如果線程池內有任務,那麽把這些任務執行完畢後,關閉線程池....

ii.shutdownNow()

這個方法表示不再接受新的任務,並把任務隊列中的任務直接移出掉,如果有正在執行的,嘗試進行停止...

大家自己試著運行下面的代碼就了解其中到底是怎麽回事了...

技術分享圖片
package executor;

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

public class Executor {

    /**
     * @param args
     * 
     */
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ExecutorService executorService=Executors.newFixedThreadPool(1);//定義了線程池中最大存在的線程數目...
        
        //添加了第一個任務...這個執行三次停止...
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                int j=0;
                while(true){
                    j++;
                    System.out.println("aa");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    if(j==3){
                        break;
                    }
                }
            }
        });
        
        //添加第二個任務,由於使用executorService.shutdown(),由於它的加入是在這個方法調用之前的,因此這個任務也會被執行...
        //如果我們使用了executorService.shutdownNow();方法,就算是他在之前加入的,由於調用了executorService.shutdownNow()方法
        //那麽這個任務將直接被移出隊列並且不會被執行...
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                int i=0;
                while(true){
                    i++;
                    System.out.println("bb");
                    if(i==3){
                        break;
                    }
                }
            }
        });
        executorService.shutdown();//這裏無論使用了那種方法,都會拋出一個異常...
        /*
         * @param
         * 第三個任務...只有當第二個任務被執行三次之後才能被執行...
         * 由於三次前,線程池已經滿了,這個任務是輪不到被執行的..只能排隊進行等待. 
         * 三次之後,第二個任務被終止,也就是線程池中出現了空閑的狀態,所以這個任務將被放入到線程池中執行...
         * */
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    
                    System.out.println("cc");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
    }

}
技術分享圖片

ThreadPoolExecutor 的三種提交任務方式