Java併發編程——工具類

話不多說,直接擼代碼。

一、CountDownLatch使用示例

package com.alex.latch;

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

/**
 * @author: alex
 * @Date: 2019/3/20
 * @Description: 演示CountDownLatch的使用方法。
 * 龜兔賽跑的例子,兔子先到達終點,但是比賽繼續,需要等待烏龜也到達終點後,比賽結束。
 */
public class RunningRace {

    private static final CountDownLatch latch = new CountDownLatch(2);
    private static final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);

    /**
     * 兔子的線程類
     */
    static class RabbitThread implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("兔子線程" + Thread.currentThread().getName() + "開始起跑");
                Thread.sleep(1000);  //兔子跑的快,所以只用1s就可以到達終點
                System.out.println("兔子線程" + Thread.currentThread().getName() + "跑到終點");
                latch.countDown();  //兔子到達終點,計數減一
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 烏龜線程
     */
    static class TurtleThread implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("烏龜線程" + Thread.currentThread().getName() + "開始起跑");
                Thread.sleep(3000); //烏龜跑的慢,所以用3s纔可以到達終點
                System.out.println("烏龜線程" + Thread.currentThread().getName() + "跑到終點");
                latch.countDown();  //烏龜到達終點,計數減一
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        try {
            System.out.println("龜兔賽跑比賽開始...");
            fixedThreadPool.submit(new RabbitThread()); //兔子起跑
            fixedThreadPool.submit(new TurtleThread()); //烏龜起跑
            /**
             * 1、要保證兔子和烏龜都到達終點後,纔可以完成比賽。
             * 2、要主要使用await方法,而不是wait方法。wait方法是等待線程,在此處是不起作用的。
             * 3、await方法是CountDownLatch的阻塞方法。當前線程等到鎖存器計數到零,繼續執行程序。
             */
            latch.await();
            System.out.println("龜兔賽跑比賽結束....");
            fixedThreadPool.shutdownNow();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運行結果:

龜兔賽跑比賽開始...
兔子線程pool-1-thread-1開始起跑
烏龜線程pool-1-thread-2開始起跑
兔子線程pool-1-thread-1跑到終點
烏龜線程pool-1-thread-2跑到終點
龜兔賽跑比賽結束....

2、CyclicBarrier使用示例

package com.alex.cyclicbarrier;

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

/**
 * @author: alex
 * @Date: 2019/3/20
 * @Description: 演示CyclicBarrier用法。利用考試的例子來說明CyclicBarrier的用法。
 * 場景描述:
 * 模擬有5個學生進行考試,當五個學生都交卷之後,老師開始批卷。
 */
public class ExamDemo {
    private static final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

    public static void main(String[] args) {
        try {
            /**
             * CyclicBarrier有兩個構造方法
             * public CyclicBarrier(int parties, Runnable barrierAction) {} 當parties個線程執行完成後,隨機獲取一個線程去執行barrierAction動作
             * public CyclicBarrier(int parties) {}當parties個線程執行完成後,纔可以進行後續操作。(與CountDownLatch類似)
             */
            CyclicBarrier barrier  = new CyclicBarrier(5,new Thread(new TeacherThread()));
            fixedThreadPool.submit(new StudentThread(1000,barrier));
            fixedThreadPool.submit(new StudentThread(2000,barrier));
            fixedThreadPool.submit(new StudentThread(3000,barrier));
            fixedThreadPool.submit(new StudentThread(4000,barrier));
            fixedThreadPool.submit(new StudentThread(5000,barrier));
            fixedThreadPool.shutdown();
//            for(int i=0;i<5;i++) {
//               new Thread(new StudentThread(1000*(i+1),barrier)).start();
//            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 學生線程,負責答題交卷
     */
    static class StudentThread implements Runnable {
        private CyclicBarrier cyclicBarrier;
        private long answerTime = 1000;

        public StudentThread(long answerTime,CyclicBarrier cyclicBarrier) {
            this.answerTime = answerTime;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                System.out.println("學生線程" + Thread.currentThread().getName() + "開始答題...");
                Thread.sleep(answerTime);  //答題時間
                System.out.println("學生線程" + Thread.currentThread().getName() + "結束答題...交卷");
                cyclicBarrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 老師線程,負責批卷
     */
    static class TeacherThread implements Runnable {
        @Override
        public void run() {
            System.out.println("老師線程" + Thread.currentThread().getName() + "開始批卷...");
        }
    }
}


運行結果:

學生線程pool-1-thread-1開始答題...
學生線程pool-1-thread-2開始答題...
學生線程pool-1-thread-3開始答題...
學生線程pool-1-thread-5開始答題...
學生線程pool-1-thread-4開始答題...
學生線程pool-1-thread-1結束答題...交卷
學生線程pool-1-thread-2結束答題...交卷
學生線程pool-1-thread-3結束答題...交卷
學生線程pool-1-thread-4結束答題...交卷
學生線程pool-1-thread-5結束答題...交卷
老師線程pool-1-thread-5開始批卷...

3、Semaphore使用示例

package com.alex.semaphore;

import java.util.LinkedList;
import java.util.concurrent.Semaphore;

/**
 * @author: alex
 * @Date: 2019/3/20
 * @Description: 演示Semaphore的使用方法,用來控制同時訪問特定資源的線程數量。
 * 通過acquire()獲取一個許可,如果沒有就等待,而release()釋放一個許可。
 */
public class DBConnectionPool {

    //使用LinkedList存放連接實體,先入先出,取連接從頭獲取,歸還連接放入到隊尾
    private LinkedList<DBConnection> connPoolList = null;
    //信號量
    private Semaphore semaphore = null;

    /**
     * 構造方法
     * @param size 連接池的大小
     */
    public DBConnectionPool(int size) {
        //初始化連接池
        connPoolList = new LinkedList<>();
        for (int i = 0; i < size; i++) {
            connPoolList.push(new DBConnection("conn_" + i)); //生成連接放入到池中
        }
        //初始化信號量
        semaphore = new Semaphore(size);
    }

    /**
     * 獲取連接
     *
     * @return 連接實體
     */
    public DBConnection getConnection() {
        try {
            semaphore.acquire(); //獲取一個許可,信號量減一
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        DBConnection conn = null;
        synchronized (connPoolList) {
            //同步獲取一個連接
            conn = connPoolList.removeFirst(); //從集合中刪除並返回第一個元素
        }
        return conn;
    }

    /**
     * 釋放連接
     *
     * @param conn 連接
     */
    public void releaseConnection(DBConnection conn) {
        synchronized (connPoolList) {
            connPoolList.addLast(conn); //將指定的元素追加到此集合的末尾。
        }
        semaphore.release();  //釋放許可,信號量加一
    }
}


DBConnection實體
package com.alex.semaphore;

import java.util.LinkedList;
import java.util.concurrent.Semaphore;

/**
 * @author: alex
 * @Date: 2019/3/20
 * @Description: 演示Semaphore的使用方法,用來控制同時訪問特定資源的線程數量。
 * 通過acquire()獲取一個許可,如果沒有就等待,而release()釋放一個許可。
 */
public class DBConnectionPool {

    //使用LinkedList存放連接實體,先入先出,取連接從頭獲取,歸還連接放入到隊尾
    private LinkedList<DBConnection> connPoolList = null;
    //信號量
    private Semaphore semaphore = null;

    /**
     * 構造方法
     * @param size 連接池的大小
     */
    public DBConnectionPool(int size) {
        //初始化連接池
        connPoolList = new LinkedList<>();
        for (int i = 0; i < size; i++) {
            connPoolList.push(new DBConnection("conn_" + i)); //生成連接放入到池中
        }
        //初始化信號量
        semaphore = new Semaphore(size);
    }

    /**
     * 獲取連接
     *
     * @return 連接實體
     */
    public DBConnection getConnection() {
        try {
            semaphore.acquire(); //獲取一個許可,信號量減一
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        DBConnection conn = null;
        synchronized (connPoolList) {
            //同步獲取一個連接
            conn = connPoolList.removeFirst(); //從集合中刪除並返回第一個元素
        }
        return conn;
    }

    /**
     * 釋放連接
     *
     * @param conn 連接
     */
    public void releaseConnection(DBConnection conn) {
        synchronized (connPoolList) {
            connPoolList.addLast(conn); //將指定的元素追加到此集合的末尾。
        }
        semaphore.release();  //釋放許可,信號量加一
    }
}


測試類:

package com.alex.semaphore;

/**
 * @author: alex
 * @Date: 2019/3/20
 * @Description: 模擬場景:數據庫連接池的大小爲2,當有3個線程同時請求獲取連接時,有2個請求可以獲得連接池,1個請求處於等待狀態。
 * 當有連接歸還後,處於等待的請求即可獲得連接。
 */
public class DBConnectionTest {

    public static void main(String[] args) {
        DBConnectionPool pool = new DBConnectionPool(2);
        /**
         * 開啓3個線程請求分配連接
         */
        for (int i = 0; i < 3; i++) {
            new Thread() {
                public void run() {
                    try {
                        System.out.println("線程" + Thread.currentThread().getName() + "準備獲取數據庫連接...");
                        DBConnection connection = pool.getConnection();
                        System.out.println("線程" + Thread.currentThread().getName() + "拿到數據庫連接:" + connection.getId());
                        //模擬處理業務邏輯,每個連接使用的時間不相等
                        int index = Integer.valueOf(connection.getId().split("_")[1]);
                        Thread.sleep(1000 * index);
                        pool.releaseConnection(connection);
                        System.out.println("線程" + Thread.currentThread().getName() + "歸還數據庫連接...");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

運行結果:

線程Thread-0準備獲取數據庫連接...
線程Thread-1準備獲取數據庫連接...
線程Thread-0拿到數據庫連接:conn_1
線程Thread-2準備獲取數據庫連接...
線程Thread-1拿到數據庫連接:conn_0
線程Thread-1歸還數據庫連接...
線程Thread-2拿到數據庫連接:conn_0
線程Thread-2歸還數據庫連接...
線程Thread-0歸還數據庫連接...

都是一些工具類的簡單使用。配合api就可以看懂,不做過多的解釋。

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