多線程高級的工具

java.util.concurrent中更高級的工具分成三類:

Executor Framework
併發集合(Concurrent Collection)
同步器(Synchronizer)


一、executor和task優先於線程:

在Java 1.5 中提供了java.util.concurrent包,在這個包中包含了Executor Framework框架,
這是一個很靈活的基於接口的任務執行工具。該框架提供了非常方便的調用方式和強大的功能,
如:
//創建一個單線程執行器對象。
ExecutorService executor = Executors.newSingleThreadExecutor();

//提交一個待執行的任務。
executor.execute(runnable);
//使執行器優雅的終止。
executor.shutdown();

事實上,Executors對象還提供了更多的工廠方法,

如適用於小型服務器的:Executors.newCachedThreadPool()工廠方法
該方法創建的執行器實現類對於小型服務器來說還是比較有優勢的,
因爲在其內部實現中並沒有提供任務隊列,而是直接將任務提交給當前可用的線程,
如果此時沒有可用的線程了,則創建一個新線程來執行該任務。
因此在任務數量較多的大型服務器上,由於該機制創建了大量的工作者線程,
這將會導致系統的整體運行效率下降。對於該種情況,
Executors提供了另外一個工廠方法Executors.newFixedThreadPool(),
該方法創建的執行器實現類的內部提供了任務隊列,用於任務緩衝。
相比於java.util.Timer,該框架也提供了一個更爲高效的執行器實現類,
通過工廠方法Executors.ScheduledThreadPool()可以創建該類。
它提供了更多的內部執行線程,這樣在執行耗時任務是,其定時精度要優於Timer類。

-------------------------------------------

二、併發工具優先於wait和notify:

對於同步器,concurrent包中給出了四種主要的同步器對象:
CountDownLatch、Semaphore、CyclicBarrier和Exchanger。
這裏前兩種比較常用。在該條目中我們只是簡單介紹一個CountDownLatch的優勢。

使用場景:

該類允許一個或者多個線程等待一個或者多個線程來做某些事情。

CountDownLatch的唯一構造函數帶有一個int類型的參數 ,
這個int參數是指允許所有在等待的線程被處理之前,必須在鎖存器上調用countDown方法的次數。

代碼如下:

package countDownLatch_test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import threadPool.executorDemo.RejectedTaskController;
/**
 * 
 * 同步器對象:CountDownLatch
 * 併發工具優先於wait和notify
 */
public class Driver {
    
    /**
     * 工作線程數
     */
    private static final int WORK_NUM = 5;
    
    public static void main(String[] args) {
        
        //參數 1 爲倒計數次數,即startSignal.countDown() 1 次,startSignal.await()倒計數爲0,線程釋放
        CountDownLatch startSignal = new CountDownLatch(1);
        
        //參數 5 爲倒計數次數,即需要doneSignal.countDown()的次數,doneSignal.await()倒計數爲0,線程釋放
        CountDownLatch doneSignal = new CountDownLatch(WORK_NUM);
        
        //創建一個擁有5個線程的線程池
        ThreadPoolExecutor exe = (ThreadPoolExecutor)Executors.newFixedThreadPool(WORK_NUM);
        
        //添加線程池關閉後出現的異常處理
        exe.setRejectedExecutionHandler(new RejectedTaskController());
        
        for (int i = 0; i < 5; ++i) {
            //在未來某個時間執行給定的命令
            exe.execute(new Worker(i, startSignal, doneSignal));
        }
        
        //在工作線程未啓動前做一些事前準備
        doSomethingElse();
        
        //倒數一次,startSignal.await()倒計數爲0,線程釋放
        startSignal.countDown();
        
        try {
            //等待工作線程全部完畢,也就是doneSignal.countDown()爲5次
            doneSignal.await();
            //工作線程完畢後的事後處理
            doSomethingElse();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            //順序關閉
            exe.shutdown();
        }
    }
    
    private static void doSomethingElse() {
        System.out.println("doSomethingElse.....");
    }
}

package countDownLatch_test;

import java.util.concurrent.CountDownLatch;

public class Worker implements Runnable{
    
    /**
     * 工作編號
     */
    private int id;
    
    private final CountDownLatch startSignal;
    
    private final CountDownLatch doneSignal;
    
    public Worker(int id ,CountDownLatch startSignal, CountDownLatch doneSignal) {
        this.id = id;
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
    }
    
    public void run() {
        try {
            //等待倒數到0,如果沒有到達0,就阻塞等待
            startSignal.await();
            doWork();
            //倒數一次
            doneSignal.countDown();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        } 
    }
    
    private void doWork() {
        System.out.println("工作 :"+id+"  doWork ...."); 
    }
    
}
public class RejectedTaskController implements RejectedExecutionHandler {
    
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        
        //打印線程信息
        System.out.printf("RejectedTaskController: The task %s has been rejected\n", r.toString());
        
        //打印線程池信息
        System.out.printf("RejectedTaskController: %s\n", executor.toString());
        
        //如果此執行程序處於在 shutdown 或 shutdownNow 之後正在終止但尚未完全終止的過程中,則返回 true
        System.out.printf("RejectedTaskController: Terminating: %s\n", executor.isTerminating());
        
        //如果關閉後所有任務都已完成,則返回 true
        System.out.printf("RejectedTaksController: Terminated: %s\n", executor.isTerminated());
    }
}

----------------------------------------------

三 併發集合;
相比於java.util中提供的集合類,java.util.concurrent中提供的併發集合就有更好的併發性,
其性能通常數倍於普通集合,如ConcurrentHashMap等。
換句話說,除非有極其特殊的原因存在,否則在併發的情況下,一定要優先選擇ConcurrentHashMap,
而不是Collections.syschronizedmap或者Hashtable。
java.util.concurrent包中還提供了阻塞隊列,
該隊列極大的簡化了生產者線程和消費者線程模型的編碼工作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章