併發編程之同步工具

同步工具類可以是任何一個對象,只要能夠根據其自身的狀態來協調線程的控制流就行,阻塞隊列就可以作爲同步工具類,java平臺類庫中還包含其他一些同步工具類,不過這裏主要分享CountDownLatch、CyclicBarrier、Semaphore和 Exchanger。

CountDownLatch

CountDownLatch允許一個或多個線程等待其他線程完成操作。它維護了一個鎖存器的計數,當計數爲0的時候,那些處於wait狀態的線程才能執行。 CountDownLatch是一種閉鎖,閉鎖相當於一扇門,在閉鎖到達結束狀態之前,門一直是關閉的,並且沒有任何線程能通過,當到達結束狀態時,門纔會打開並允許所有的線程通過。

假如有這樣一個需求:一個文本文件,有N行數據,通過多線程計算N行數據的和。可以通過CountDownLatch實現,先創建N個線程對每一行求和,最後主線程求整個文本文件的數據之和:

public class CountDownLatchExp {
    private int [] nums;
    public CountDownLatchExp(int lines){
        nums = new int[lines];
    }
    public void calc (String line,int index,CountDownLatch latch){
        String[] datas = line.split(",");
        int total = 0;
        for(String data:datas){
            total += Integer.parseInt(data);
        }
        //每一行的和放到數組對應位置
		nums[index] = total; 		 
		System.out.println(Thread.currentThread().getName()+"計算完成,結果爲"+total); 
		latch.countDown(); //一行計算完就將鎖存器減1
    }
    public static void main(String[] args) {
		List<String> lines = readlines();//讀文件
		int lineCount = lines.size();
		CountDownLatch latch = new CountDownLatch(lineCount);
		CountDownLatchExp demo = new CountDownLatchExp(lineCount); 
		for(int i=0;i<lineCount;i++){
            final int j = i;
            new Thread(new Runnable() {
				@Override
                public void run() {
                    demo.calc(lines.get(j), j,latch);
                }
            }).start();
		}
		latch.await(); //當鎖存器不爲0就一直等待 
		demo.sum();//對數組求和
	} 
}

CyclicBarrier

CyclicBarrier的字面意思是可循環的屏障。它要做的事情是,讓一組線程到達一個屏障(也可以叫同步點)時被阻 塞,直到最後一個線程到達屏障時,屏障纔會開門,所有被屏障攔截的線程纔會繼續運行。也就是讓一組線程到達 一個同步點後再一起繼續運行

CyclicBarrier提供一個更高級的構造函數 CyclicBarrier(int parties, Runnable barrierAction) ,用於在線程到達同步點時,優先執行線程barrierAction,這樣可以更加方便的處理一些複雜的業務場景, 且CyclicBarrier可用於多線程計算數據,最後合併計算結果的場景。

public class CalculateExp {
    private int [] nums;
    public CalculateExp(int lines){
        nums = new int[lines];
    }
    public void calc (String line, int index, CyclicBarrier barrier){
        String[] datas = line.split(",");
        int total = 0;
        for(String data:datas){
            total += Integer.parseInt(data);
        }
		nums[index] = total; 	
		System.out.println(Thread.currentThread().getName()+"計算完成,結果爲"+total); 
		barrier.await();
    }
    public static void main(String[] args) {
        List<String> lines = readlines();
        int lineCount = lines.size();
        CalculateExp demo = new CalculateExp(lineCount);
        CyclicBarrier barrier = new CyclicBarrier(lineCount, new Runnable() {
			@Override
			public void run() { 
				demo.sum();//數組求和
			} 
		});
        for(int i=0;i<lineCount;i++){
            final int j = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo.calc(lines.get(j), j,barrier);
                }
            }).start();
		} 
	}
}

CyclicBarrier和CountDownLatch的區別:

CountDownLatch:一個或者多個線程,等待其他多個線程完成某件事情之後才能執行; CyclicBarrier:多個線程互相等待,直到到達同一個同步點,再繼續一起執行。

CountDownLatch是計數器,只能使用一次,而CyclicBarrier的計數器提供reset功能 ,但使用reset的時候要 注意不要產生一直阻塞的線程。

Exchanger

Exchanger也是一種屏障,不過它是雙方的(Two-Party),用於進行線程間的數據交換。它提供一個同步點,在這 個同步點,兩個線程可以交換彼此的數據。這兩個線程通過 exchange方法交換數據,如果第一個線程先執行 exchange()方法,它會一直等待第二個線程也執行exchange方法,當兩個線程都到達同步點時,這兩個線程就可以交換數據,將本線程生產出來的數據傳遞給對方。

Exchanger 使用是很簡單,就提供了一個exchange操作和超時exchange操作,但實現原理不簡單,底層也通過了 CAS算法實現線程安全。

Exchanger交換分爲單槽和多槽,單個槽位在同一時刻只能用於兩個線程交換數據, 再有線程進來競爭就實行多槽交換,多槽交換允許多個線程可以同時進行兩兩數據交換,彼此之間不受影響 ,且交換的對象是不確定的。Exchanger可用於結果校對,流水線設計,生產者消費者數據交換,遺傳算法等。

//緩存交換
public class ExchangeCache {
    private final static int MAX_COUNT = 10;
    
    static class FillingLoop implements Runnable {
        private Exchanger<ConcurrentHashMap<String,String>> exchanger;
        ConcurrentHashMap<String,String> currentBuffer;
        
        public FillingLoop(Exchanger<ConcurrentHashMap<String,String>>
exchanger,ConcurrentHashMap<String,String> currentBuffer){
            this.exchanger = exchanger;
            this.currentBuffer = currentBuffer;
        }
        public void run() {
            int count = 0;
            while (currentBuffer != null) {
                if (currentBuffer.size() == MAX_COUNT)
                    currentBuffer = exchanger.exchange(currentBuffer);
                Thread.sleep(500);
                String key = ""+count;
                String value = ""+count;
				currentBuffer.put(key,value); 
				System.out.println("生產緩存:"+value); count++;
			} 
		}
	}
    static class EmptyingLoop implements Runnable {
        private Exchanger<ConcurrentHashMap<String,String>> exchanger;
        ConcurrentHashMap<String,String> currentBuffer;
        public EmptyingLoop(Exchanger<ConcurrentHashMap<String,String>>
exchanger,ConcurrentHashMap<String,String> currentBuffer){
            this.exchanger = exchanger;
            this.currentBuffer = currentBuffer;
        }
        public void run() {
        int count = 0;
            while (currentBuffer != null) {
                if (currentBuffer.size() == 0)
                    currentBuffer = exchanger.exchange(currentBuffer);
					Thread.sleep(500);
					String key = ""+count; 
					System.out.println("消費緩存:"+currentBuffer.get(key)); currentBuffer.remove(key);
					count++;
				} 
			}
    }
    
    public static void main(String[] args) {
        Exchanger<ConcurrentHashMap<String,String>> exchanger = new Exchanger<ConcurrentHashMap<String,String>>();
        ConcurrentHashMap<String,String> currentBuffer1 = new ConcurrentHashMap<String,String>(MAX_COUNT);
        ConcurrentHashMap<String,String> currentBuffer2 = new ConcurrentHashMap<String,String>(MAX_COUNT);
        new Thread(new FillingLoop(exchanger,currentBuffer1)).start();
        new Thread(new EmptyingLoop(exchanger,currentBuffer2)).start();
    }
}
  

Semaphore

Semaphore(信號量)是用來控制同時訪問特定資源的線程數量,它通過協調各個線程,以保證合理的使用公共 資源。可以用於做流量控制等。通過 acquire() 獲取一個許可,如果沒有就等待,release() 釋放一個許可。

   
public class SemaphoreExp {
    public void method(Semaphore semaphore){
		semaphore.acquire(); //進來 	
		System.out.println(Thread.currentThread().getName()+" 執行。。。"); 
		semaphore.release(); //釋放
    }
    public static void main(String[] args) {
		SemaphoreExp d = new SemaphoreExp();
		Semaphore semaphore = new Semaphore(10);//method方法同時只能10個線程執行 
		int count = 0;
		while(count<30){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    d.method(semaphore);
                }
            }).start();
			count++; 
		}
	} 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章