常用的并发工具类

在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier、Semaphore和Exchange

CountDownLatch

CountDownLatch是一个同步计数器,初始化的时候 传入需要计数的线程等待数,可以是需要等待执行完成的线程数,或者大于。
作用:用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。是一组线程等待其他的线程完成工作以后在执行,相当于加强版join,其中:

await():阻塞当前线程,等待其他线程执行完成,直到计数器计数值减到0。
countDown():负责计数器的减一。

例如:主线程等待其他六个线程执行完成,再执行主线程。

/**
 * CountDownLatch计数器演示,由外部线程控制一组线程的放行
 */
public class CountDownLatchDemo {
    
    private static CountDownLatch countDownLatch = new CountDownLatch(6);

    private static class InitThread extends Thread {

        @Override
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "开始初始化....");
            countDownLatch.countDown();
        }
    }

    private static class BusiThread extends Thread {

        @Override
        public void run() {
            try {
                System.out.println("线程" + Thread.currentThread().getName() + "准备运行....");

                //只有等countDownLatch的计数器为0,也就是其他计数器的线程都执行完了,才能执行
                countDownLatch.await();
                SleepTools.second(1);
                System.out.println("线程" + Thread.currentThread().getName() + "运行完成....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程" + Thread.currentThread().getName() + "开始初始化....");
                countDownLatch.countDown();
            }
        }, "main-2").start();

        new BusiThread().start();

        for (int i = 0; i < 5; i++) {
            new InitThread().start();
        }
        try {
            countDownLatch.await();

            System.out.println("主线程运行结束.......");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:
常用的并发工具类_第1张图片

CyclicBarrier

CyclicBarrier字面意思是栅栏,是多线程中一个重要的类,主要用于线程组内部之间的线程的相互等待问题,初始化的时候传入需要等待的线程数。
作用:让一组线程达到某个屏障被阻塞,一直到组内最后一个线程达到屏障时,屏障开放,所有被阻塞的线程才会继续运行。其中:

CyclicBarrier(int parties):初始化定义需要等待的线程数parties。
CyclicBarrier(int parties, Runnable barrierAction):当屏障开放的时候,线程barrierAction的任务会执行。

CountDownLatch和CyclicBarrier的区别:

1、countdownlatch放行由第三者控制,CyclicBarrier放行由一组线程本身控制
2、countdownlatch放行条件》=线程数,CyclicBarrier放行条件=线程数
3、CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。
4、CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset()方
法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。

例如:主线程等待5个子线程执行完成

/**
 * CyclicBarrier栅栏演示,由一组线程本身控制放行
 */
public class CyclicBarrierDemo {

    //定义一个栅栏,在栅栏放行的时候,同时可以运行CollectThread这个线程
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new CollectThread());

    private static ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
    
    private static class CollectThread extends Thread {

        @Override
        public void run() {
            System.out.println(concurrentHashMap.size());
        }
    }
    
    private static class SubThread extends Thread {
        @Override
        public void run() {
            try {
                Random random = new Random();
                long id = Thread.currentThread().getId();
                String name = Thread.currentThread().getName();
                concurrentHashMap.put(id, name);
                if (random.nextBoolean()) {
                    System.out.println("线程" + name + "正在处理中....");
                    SleepTools.second(2);
                }
                System.out.println("线程" + name + "处理结束,等待其他线程处理结束....");

                //每个子线程都会在这里阻塞,等待所有的子线程都执行到这里,才会一起放行
                cyclicBarrier.await();
                System.out.println("线程" + name + "处理完毕....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new SubThread().start();
        }

        //CyclicBarrier,不会阻塞主线程
        System.out.println("主线程.....");
    }
}

运行结果:
常用的并发工具类_第2张图片

Semaphore

Semaphore又名信号量,是操作系统中的一个概念,在Java并发编程中,信号量控制的是线程并发的数量。
作用:Semaphore管理一系列许可证。每个acquire方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个release方法增加一个许可证,这可能会释放一个阻塞的acquire方法。然而,其实并没有实际的许可证这个对象,Semaphore只是维持了一个可获得许可证的数量,主要控制同时访问某个特定资源的线程数量,多用在流量控制。
注意:其他Semaphore的底层实现就是基于AQS的共享锁实现的。

如果一个线程要访问共享资源,必须先获得信号量,如果信号量的计数器值大于1,意味
着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。如果计数器值为0,线
程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加1,之前进入休眠的线程将被唤醒并再次试图获得信号量。

例如:使用Semaphore模拟数据库连接池,Semaphore信号量是可以控制多个线程访问同一资源。

/**
 * 模拟数据库连接实现
 */
public class ConnectionImpl implements Connection {

    public static Connection getConnection() {
        return new ConnectionImpl();
    }

    @Override
    public Statement createStatement() throws SQLException {
        //模拟操作
        SleepTools.ms(10);
        return null;
    }
    //剩下的重写的方法就省略了........
 }

这个类是模拟数据库连接操作,其中重写了一个方法,休眠10毫秒,用于模拟实际操作耗时。

/**
 * 用Semaphore信号量,模拟数据库连接池,Semaphore信号量是可以控制多个线程访问同一资源
 */
public class SemaphoreDemo {

    //线程池
    private static LinkedList list = new LinkedList<>();

    //线程池的大小
    private static final int POOL_SIZE = 10;

    //useful:可以使用的数据库连接数   userless:不能使用的数据库连接数
    private static Semaphore useful, userless;

    public SemaphoreDemo() {
        useful = new Semaphore(POOL_SIZE);
        userless = new Semaphore(0);
    }

    //初始化线程池
    static {
        for (int i = 0; i < POOL_SIZE; i++) {
            list.add(new ConnectionImpl());
        }
    }


    //获取数据库连接
    public static Connection getConnection() {
        Connection connection = null;
        try {
            //可用的信号获取许可证,阻塞方法,只有当前线程获取到许可证才能放行
            useful.acquire();

            synchronized (list) {
                //取出连接池中的第一个
                connection = list.removeFirst();
            }
            //不可用的信号释放许可证,将其返回到信号量
            userless.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return connection;
    }

    //释放连接
    public static void releaseConnection(Connection connection) {
        if (connection != null) {
            System.out.println("当前有" + useful.getQueueLength() + "个线程等待数据库连接!!"
                    + "可用连接数:" + useful.availablePermits());
            try {
                //不可用的信号获取许可证
                userless.acquire();
                //放回池
                synchronized (list) {
                    list.addLast(connection);
                }
                //可用的信号释放许可证
                useful.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试:

public class TestSemphore {

    private static SemaphoreDemo semaphoreDemo = new SemaphoreDemo();

    private static class BusiThread extends Thread {
        @Override
        public void run() {
            Random r = new Random();//让每个线程持有连接的时间不一样
            long start = System.currentTimeMillis();
            Connection connection = semaphoreDemo.getConnection();
            System.out.println("Thread_" + Thread.currentThread().getId()
                    + "_获取数据库连接共耗时:" + (System.currentTimeMillis() - start) + "ms.....");
            //模拟业务操作,线程持有连接查询数据
            SleepTools.ms(100 + r.nextInt(100));
            System.out.println("查询数据完成,归还连接!");
            semaphoreDemo.releaseConnection(connection);
        }
    }


    public static void main(String[] args) {

        for (int i = 0; i < 50; i++) {
            new BusiThread().start();
        }
    }
}

运行结果:
常用的并发工具类_第3张图片

Exchange

Exchange类似于一个交换器,可以在对中对元素进行配对和交换的线程的同步点,用于两个线程间的数据交换。
具体来说,Exchanger类允许在两个线程之间定义同步点。当两个线程都到达同步点时,他们交换数据结构,因此第一个线程的数据结构进入到第二个线程中,第二个线程的数据结构进入到第一个线程中。
例如:交换两个线程各自的数据。

public class ExchangerDemo {

    private static Exchanger> exchanger = new Exchanger<>();

    private static class ExchangerClassO extends Thread {
        private Set set;

        public ExchangerClassO(Set set) {
            this.set = set;
        }

        @Override
        public void run() {
            try {
                exchange(set);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    private static class ExchangerClassT extends Thread {
        private Set set;

        public ExchangerClassT(Set set) {
            this.set = set;
        }

        @Override
        public void run() {
            try {
                exchange(set);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void exchange(Set set) throws InterruptedException {
        //交换数据,阻塞
        System.out.println("线程:" + Thread.currentThread().getName() + "交换前得值....");
        for (String s : set) {
            System.out.println(s);
        }
        exchanger.exchange(set);
        System.out.println("线程:" + Thread.currentThread().getName() + "交换后得值....");
        for (String s : set) {
            System.out.println(s);
        }
    }


    public static void main(String[] args) {
        Set setA = new HashSet<>();
        Set setB = new HashSet<>();
        setA.add("a1");
        setA.add("b1");
        setA.add("c1");

        setB.add("a2");
        setB.add("b2");
    setB.add("c2");

    new ExchangerClassO(setA).start();
    new ExchangerClassT(setB).start();
}

}

运行结果:
常用的并发工具类_第4张图片

你可能感兴趣的:(并发)