線程池ExecutorService

  說到java開發,免不了跟多線程打交道。Executor框架即是Java 5中引入的,其內部使用了線程池機制,它在java.util.cocurrent 包下,經過該框架來控制線程的啓動、執行和關閉,能夠簡化併發編程的操做。所以,在Java 5以後,經過Executor來啓動線程比使用Thread的start方法更好,除了更易管理,效率更好(用線程池實現,節約開銷)外,還有關鍵的一點:有助於避免this逃逸問題——若是咱們在構造器中啓動一個線程,由於另外一個任務可能會在構造器結束以前開始執行,此時可能會訪問到初始化了一半的對象用Executor在構造器中。java

ExecutorService,是一個接口,繼承了Executor;編程

Executor,只有一個方法:緩存

public interface Executor {
    void execute(Runnable command);
}

Executors提供了4中建立線程池:多線程

public static ExecutorService newCachedThreadPool(); 建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。 併發

public static ExecutorService newFixedThreadPool(int nThreads); 建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。 框架

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 建立一個定長線程池,支持定時及週期性任務執行。 ide

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

經過Executors的以上四個靜態工廠方法得到 ExecutorService實例,然後調用該實例的execute(Runnable command)方法便可。一旦Runnable任務傳遞到execute()方法,該方法便會自動在一個線程上執行。spa

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

public class ExecutorTest {
    public static void main(String[] args){
        ExecutorService executorService = Executors.newCachedThreadPool();   
        //ExecutorService executorService = Executors.newFixedThreadPool(3);  
//      ExecutorService executorService = Executors.newSingleThreadExecutor(); 
        for (int i = 0; i < 3; i++){ 
            RunnableTest method = new RunnableTest();
            executorService.execute(method);   
            System.out.println("########" + i + "########");   
        }   
        executorService.shutdown(); 
    }
}
public class RunnableTest implements Runnable{   
    public void run(){   
        System.out.println(Thread.currentThread().getName() + "線程執行");   
    }  
}

輸出結果:線程

########0########
########1########
########2########
pool-1-thread-2線程執行
pool-1-thread-3線程執行
pool-1-thread-1線程執行

任務分兩類:一類是實現了Runnable接口的類,一類是實現了Callable接口的類。二者均可以被ExecutorService執行,可是Runnable任務沒有返回值,而Callable任務有返回值。而且Callable的call()方法只能經過ExecutorService的submit(Callable<T> task) 方法來執行,而且返回一個 <T>Future<T>,是表示任務等待完成的 Future。

下面給出一個Executor執行Callable任務的示例代碼

public class ExecutorTest {
    public static void main(String[] args){
        ExecutorService executorService = Executors.newCachedThreadPool();   
        //ExecutorService executorService = Executors.newFixedThreadPool(3);  
//      ExecutorService executorService = Executors.newSingleThreadExecutor(); 
        List<Future<String>> resultList = new ArrayList<Future<String>>();  
        for (int i = 0; i < 6; i++){ 
            CallableTest method = new CallableTest(i);
            Future<String> future = executorService.submit(method);   
            System.out.println("########" + i + "########");   
            resultList.add(future);
        }   

        //遍歷任務的結果   
        for (Future<String> fs : resultList){   
                try{   
                    //Future返回若是沒有完成,則一直循環等待,直到Future返回完成  
                    while(!fs.isDone());
                    //打印各個線程(任務)執行的結果 
                    System.out.println(fs.get());     
                }catch(InterruptedException e){   
                    e.printStackTrace();   
                }catch(ExecutionException e){   
                    e.printStackTrace();   
                }finally{   
                    //啓動一次順序關閉,執行之前提交的任務,但不接受新任務  
                    executorService.shutdown();   
                }   
        }   
    }
}
import java.util.concurrent.Callable;

public class CallableTest implements Callable<String>{
    
    private int id; 
    
    public CallableTest(int id){   
        this.id = id;   
    }  
    
    @Override
    public String call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "線程執行");  
        return Thread.currentThread().getName() + "線程執行" + "返回id=" + id;
    }  
}

輸出結果:

pool-1-thread-1線程執行返回id=0
pool-1-thread-2線程執行返回id=1
pool-1-thread-3線程執行返回id=2
pool-1-thread-4線程執行返回id=3
pool-1-thread-2線程執行返回id=4
pool-1-thread-4線程執行返回id=5

 

從結果中能夠一樣能夠看出,submit也是首先選擇空閒線程來執行任務,若是沒有,纔會建立新的線程來執行任務。另外,須要注意:若是Future的返回還沒有完成,則get()方法會阻塞等待,直到Future完成返回,能夠經過調用isDone()方法判斷Future是否完成了返回。

相關文章
相關標籤/搜索