線程併發工具類(Fork-Join,CountDownLatch,CyclicBarrier,Semaphore,Exchange)

Fork-Join

分而治之:規模爲N的問題,N<閾值,直接解決,N>閾值,將N分解爲K個小規模子問題,子問題互相對立,與原問題形式相同,將子問題的解合併得到原問題的解

WorkStealing:所謂 Work-Stealing,在 ForkJoinPool 中的實現爲:線程池中每個線程都有一個互不影響的任務隊列(雙端隊列),線程每次都從自己的任務隊列的隊頭中取出一個任務來運行;如果某個線程對應的隊列已空並且處於空閒狀態,而其他線程的隊列中還有任務需要處理但是該線程處於工作狀態,那麼空閒的線程可以從其他線程的隊列的隊尾取一個任務來幫忙運行 —— 感覺就像是空閒的線程去偷人家的任務來運行一樣,所以叫 “工作竊取”

ForkJoinTask<V> 用來專門定義 Fork/Join 型任務 —— 完成將大任務分割爲小任務以及合併結果的工作。一般我們不需要直接使用 ForkJoinTask,而是通過繼承它的子類 RecursiveActionRecursiveTask 並實現對應的抽象方法 —— compute ,來定義我們自己的任務。其中,RecursiveAction 是不帶返回值的 Fork/Join 型任務,所以使用此類任務並不產生結果,也就不涉及到結果的合併;而 RecursiveTask 是帶返回值的 Fork/Join 型任務,使用此類任務的話,在任務結束前,我們需要進行結果的合併。其中,通過 ForkJoinTask 的 fork 方法,我們可以產生子任務並執行;通過 join 方法,我們可以獲得子任務的結果

在這裏插入圖片描述
Fork-Join同步用法以及有返回值得情況:【統計整形數組元素和】

public class SumArray {
    private static class SumTask extends RecursiveTask<Integer>{

        private final static int THRESHOLD = MakeArray.ARRAY_LENGTH/10;
        private int[] src; //表示我們要實際統計的數組
        private int fromIndex;//開始統計的下標
        private int toIndex;//統計到哪裏結束的下標

        public SumTask(int[] src, int fromIndex, int toIndex) {
            this.src = src;
            this.fromIndex = fromIndex;
            this.toIndex = toIndex;
        }

		@Override
		protected Integer compute() {
			if(toIndex-fromIndex < THRESHOLD) {
				int count = 0;
				for(int i=fromIndex;i<=toIndex;i++) {
			    	count = count + src[i];
				}
				return count;
			}else {
				//fromIndex....mid....toIndex
				//1...................70....100
				int mid = (fromIndex+toIndex)/2;
				SumTask left = new SumTask(src,fromIndex,mid);
				SumTask right = new SumTask(src,mid+1,toIndex);
				invokeAll(left,right);
				return left.join()+right.join();
			}
		}
    }


    public static void main(String[] args) {

        ForkJoinPool pool = new ForkJoinPool();
        //生成隨機數組
        int[] src = MakeArray.makeArray();

        SumTask innerFind = new SumTask(src,0,src.length-1);

        long start = System.currentTimeMillis();

        pool.invoke(innerFind);//同步調用
        System.out.println("Task is Running.....");

        System.out.println("The count is "+innerFind.join()
                +" spend time:"+(System.currentTimeMillis()-start)+"ms");

    }
}

Fork-Join異步用法以及無返回值得情況:

public class FindDirsFiles extends RecursiveAction{

    private File path;//當前任務需要搜尋的目錄

    public FindDirsFiles(File path) {
        this.path = path;
    }

    public static void main(String [] args){
        try {
            // 用一個 ForkJoinPool 實例調度總任務
            ForkJoinPool pool = new ForkJoinPool();
            FindDirsFiles task = new FindDirsFiles(new File("F:/"));

            pool.execute(task);//異步調用

            System.out.println("Task is Running......");
            Thread.sleep(1);
            int otherWork = 0;
            for(int i=0;i<100;i++){
                otherWork = otherWork+i;
            }
            System.out.println("Main Thread done sth......,otherWork="+otherWork);
            task.join();//阻塞的方法
            System.out.println("Task end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

	@Override
	protected void compute() {
		
		List<FindDirsFiles> subTasks = new ArrayList<>();
		
		File[] files = path.listFiles();
		if(files!=null) {
			for(File file:files) {
				if(file.isDirectory()) {
					subTasks.add(new FindDirsFiles(file));
				}else {
					//遇到文件,檢查
					if(file.getAbsolutePath().endsWith("txt")) {
						System.out.println("文件:"+file.getAbsolutePath());
					}
				}
			}
			if(!subTasks.isEmpty()) {
				for(FindDirsFiles subTask:invokeAll(subTasks)) {
					subTask.join();//等待子任務執行完成
				}
			}
		}
	}
}

CountDownLatch

一個線程或一組線程等待其他的線程完成工作以後再執行
await:用來等待
countDown:負責計數器減1
在這裏插入圖片描述

//5個線程,6個countDown點
public class UseCountDownLatch {
	
	static CountDownLatch latch = new CountDownLatch(6);

	//初始化線程(只有一步,有4個)
    private static class InitThread implements Runnable{

        @Override
        public void run() {
        	System.out.println("Thread_"+Thread.currentThread().getId()
        			+" ready init work......");
        	latch.countDown();//初始化線程完成工作了,countDown方法只扣減一次;
            for(int i =0;i<2;i++) {
            	System.out.println("Thread_"+Thread.currentThread().getId()
            			+" ........continue do its work");
            }
        }
    }
    
    //業務線程
    private static class BusiThread implements Runnable{

        @Override
        public void run() {
        	try {
				latch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
            for(int i =0;i<3;i++) {
            	System.out.println("BusiThread_"+Thread.currentThread().getId()
            			+" do business-----");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
    	//單獨的初始化線程,初始化分爲2步,需要扣減兩次
        new Thread(new Runnable() {
            @Override
            public void run() {
            	SleepTools.ms(1);
                System.out.println("Thread_"+Thread.currentThread().getId()
            			+" ready init work step 1st......");
                latch.countDown();//每完成一步初始化工作,扣減一次
                System.out.println("begin step 2nd.......");
                SleepTools.ms(1);
                System.out.println("Thread_"+Thread.currentThread().getId()
            			+" ready init work step 2nd......");
                latch.countDown();//每完成一步初始化工作,扣減一次
            }
        }).start();
        new Thread(new BusiThread()).start();
        for(int i=0;i<=3;i++){
            Thread thread = new Thread(new InitThread());
            thread.start();
        }

        latch.await();
        System.out.println("Main do ites work........");
    }
}

執行結果:

Thread_16 read init work......
Thread_14 read init work......
Thread_16 ........continue do its work
Thread_15 read init work......
Thread_15 ........continue do its work
Thread_13 ........continue do its work
Thread_13 ........continue do its work
Thread_15 ........continue do its work
Thread_16 ........continue do its work
Thread_14 ........continue do its work
Thread_14 ........continue do its work
Thread_11 ready init work step 1nd......
begin step 2nd.......
Thread_11 ready init work step 2nd......
Main do ites work........
BusiThread_12 do business-----
BusiThread_12 do business-----
BusiThread_12 do business-----

CyclicBarrier

讓一組線程到達某個屏障,被阻塞,一直到組內最後一個線程到達屏障時,屏障開放,所有阻塞的線程會繼續執行

public class UseCyclicBarrier {
	
	private static CyclicBarrier barrier 
		= new CyclicBarrier(5,new CollectThread());
	
    private static ConcurrentHashMap<String,Long> resultMap
            = new ConcurrentHashMap<>();//存放子線程工作結果的容器

    public static void main(String[] args) {
        for(int i=0;i<=4;i++){
            Thread thread = new Thread(new SubThread());
            thread.start();
        }

    }

    //負責屏障開放以後的工作
    private static class CollectThread implements Runnable{

        @Override
        public void run() {
            StringBuilder result = new StringBuilder();
            for(Map.Entry<String,Long> workResult:resultMap.entrySet()){
            	result.append("["+workResult.getValue()+"]");
            }
            System.out.println(" the result = "+ result);
            System.out.println("do other business........");
        }
    }

    //工作線程
    private static class SubThread implements Runnable{

        @Override
        public void run() {
        	long id = Thread.currentThread().getId();//線程本身的處理結果
            resultMap.put(Thread.currentThread().getId()+"",id);
            Random r = new Random();//隨機決定工作線程的是否睡眠
            try {
                if(r.nextBoolean()) {
                	Thread.sleep(2000+id);
                	System.out.println("Thread_"+id+" ....do something ");
                }
                System.out.println(id+"....is await");
                barrier.await();
            	Thread.sleep(1000+id);
                System.out.println("Thread_"+id+" ....do its business ");
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}

執行結果:

11....is await
13....is await
14....is await
15....is await
Thread_12 ....do something
12....is await
 the result = [11][12][13][14][15]
do other business........
Thread_11 ....do its business
Thread_12 ....do its business
Thread_13 ....do its business
Thread_14 ....do its business
Thread_15 ....do its business

CountDownLatch 是由外部線程決定是否可以繼續執行,而CyclicBarrier是由內部線程決定大家是否可以執行

Semaphore

控制同時訪問某個特定資源的線程數量,用在流量控制

使用semaphore演示數據庫連接池:

//使用semaphore實現數據庫連接池
public class DBPoolSemaphore {
	
	private final static int POOL_SIZE = 10;
	private final Semaphore useful;//useful表示可用的數據庫連接
	
	public DBPoolSemaphore() {
		this.useful = new Semaphore(POOL_SIZE);
	}
	
	//存放數據庫連接的容器
	private static LinkedList<Connection> pool = new LinkedList<Connection>();
	//初始化池
	static {
        for (int i = 0; i < POOL_SIZE; i++) {
        	//SqlConnectImpl.fetchConnection()
        	//class SqlConnectImpl implements Connection
        	//返回一個自定義實現的Connection
            pool.addLast(SqlConnectImpl.fetchConnection());
        }
	}

	/*歸還連接*/
	public void returnConnect(Connection connection) throws InterruptedException {
		if(connection!=null) {
			System.out.println("當前有"+useful.getQueueLength()+"個線程等待數據庫連接!!"
					+"可用連接數:"+useful.availablePermits());
			synchronized (pool) {
				pool.addLast(connection);
			}	
			useful.release();
		}
	}
	
	/*從池子拿連接*/
	public Connection takeConnect() throws InterruptedException {
		useful.acquire();
		Connection conn;
		synchronized (pool) {
			conn = pool.removeFirst();
		}
		return conn;
	}
}

測試數據庫連接池:

public class AppTest {

	private static DBPoolSemaphore dbPool = new DBPoolSemaphore();
	
	//業務線程
	private static class BusiThread extends Thread{
		@Override
		public void run() {
			Random r = new Random();//讓每個線程持有連接的時間不一樣
			long start = System.currentTimeMillis();
			try {
				Connection connect = dbPool.takeConnect();
				System.out.println("Thread_"+Thread.currentThread().getId()
						+"_獲取數據庫連接共耗時【"+(System.currentTimeMillis()-start)+"】ms.");
				Thread.sleep(100+r.nextInt(100));//模擬業務操作,線程持有連接查詢數據
				System.out.println("查詢數據完成,歸還連接!");
				dbPool.returnConnect(connect);
			} catch (InterruptedException e) {
			}
		}
	}
	
	public static void main(String[] args) {
        for (int i = 0; i < 50; i++) {
            Thread thread = new BusiThread();
            thread.start();
        }
	}	
}

執行結果:

Thread_11_獲取數據庫連接共耗時【0】ms.
Thread_12_獲取數據庫連接共耗時【0】ms.
Thread_13_獲取數據庫連接共耗時【0】ms.
Thread_14_獲取數據庫連接共耗時【0】ms.
Thread_15_獲取數據庫連接共耗時【0】ms.
Thread_16_獲取數據庫連接共耗時【0】ms.
Thread_17_獲取數據庫連接共耗時【0】ms.
Thread_18_獲取數據庫連接共耗時【0】ms.
Thread_19_獲取數據庫連接共耗時【0】ms.
Thread_20_獲取數據庫連接共耗時【0】ms.
查詢數據完成,歸還連接!
當前有40個線程等待數據庫連接!!可用連接數:0
Thread_21_獲取數據庫連接共耗時【101】ms.
查詢數據完成,歸還連接!
當前有39個線程等待數據庫連接!!可用連接數:0
Thread_22_獲取數據庫連接共耗時【101】ms.
查詢數據完成,歸還連接!
當前有38個線程等待數據庫連接!!可用連接數:0
Thread_23_獲取數據庫連接共耗時【104】ms.
查詢數據完成,歸還連接!
當前有37個線程等待數據庫連接!!可用連接數:0
Thread_24_獲取數據庫連接共耗時【125】ms.
查詢數據完成,歸還連接!
當前有36個線程等待數據庫連接!!可用連接數:0
Thread_25_獲取數據庫連接共耗時【142】ms.
查詢數據完成,歸還連接!
當前有35個線程等待數據庫連接!!可用連接數:0
Thread_26_獲取數據庫連接共耗時【163】ms.
查詢數據完成,歸還連接!
當前有34個線程等待數據庫連接!!可用連接數:0
Thread_27_獲取數據庫連接共耗時【164】ms.
查詢數據完成,歸還連接!
當前有33個線程等待數據庫連接!!可用連接數:0
Thread_28_獲取數據庫連接共耗時【166】ms.
查詢數據完成,歸還連接!
當前有32個線程等待數據庫連接!!可用連接數:0
Thread_29_獲取數據庫連接共耗時【174】ms.
查詢數據完成,歸還連接!
當前有31個線程等待數據庫連接!!可用連接數:0
Thread_30_獲取數據庫連接共耗時【176】ms.
查詢數據完成,歸還連接!

Exchange

用於兩個線程間的數據交換

public class UseExchange {
    private static final Exchanger<Set<String>> exchange = new Exchanger<Set<String>>();

    public static void main(String[] args) {

    	//第一個線程
        new Thread(new Runnable() {
            @Override
            public void run() {
            	Set<String> setA = new HashSet<String>();//存放數據的容器
                try {
                	/*添加數據
                	 * set.add(.....)
                	 * */
                	setA = exchange.exchange(setA);//交換set
                	/*處理交換後的數據*/
                } catch (InterruptedException e) {
                }
            }
        }).start();

      //第二個線程
        new Thread(new Runnable() {
            @Override
            public void run() {
            	Set<String> setB = new HashSet<String>();//存放數據的容器
                try {
                	/*添加數據
                	 * set.add(.....)
                	 * set.add(.....)
                	 * */
                	setB = exchange.exchange(setB);//交換set
                	/*處理交換後的數據*/
                } catch (InterruptedException e) {
                }
            }
        }).start();

    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章