Java并发之并发工具类

在Java并发编程中我们往往需要一些工具类来实现我们的功能,JDK给我们提供了一些工具类,合理地使用它们能帮忙我们快速地完成功能。下面来学习一下这些工具类的使用吧!

一.等待多线程完成的CountDownLatch

CountDownLatch允许一个或多个线程等待其他线程完成操作。

假设有这样一个需求,主线程需要等待其他几个线程完成后再继续执行。

一种方案是使用join方法。在主线程中调用其它线程的join方法,每调用一个join,如果该线程没有结束主线程就会阻塞在这里。直到该线程结束主线程才变为运行态。关于join方法的介绍请参考Java并发之基础知识的最后一部分内容。

这个需求还可以使用CountDownLatch来实现。

1.1 CountDownLatch用法介绍

//使用之前先构造一个CDL
//注意这里的入参2。这个参数表示需要计数2次,这个CDL才结束
CountDownLatch c = new CountDownLatch(2);

//计数  这个例子中总共需要调用两次countDown(),计数才结束
c.countDown()

//在主线程中调用,调用了这个方法后如果c没有计数到0则阻塞,直到计数为0,唤醒线程
c.await()

1.2 具体的例子

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    static CountDownLatch c = new CountDownLatch(2);

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("线程1开始执行!");
                    Thread.sleep(3000);
                    System.out.println("线程1结束执行!");
                    c.countDown();
                } catch (InterruptedException e) {

                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("线程2开始执行!");
                    Thread.sleep(5000);
                    System.out.println("线程2结束执行!");
                    c.countDown();
                } catch (InterruptedException e) {

                }
            }
        });
        t1.start();
        t2.start();
        try {
            System.out.println("开始等待线程1、2结束");
            c.await();
            System.out.println("线程1、2结束");
        } catch (InterruptedException e) {

        }
    }
}

执行结果

开始等待线程1、2结束
线程2开始执行!
线程1开始执行!
线程1结束执行!
线程2结束执行!
线程1、2结束

二.同步屏障CycliBarrier

CycliBarrier这个单词的字面意思是可循环使用的屏障的。它的功能是:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被阻塞的线程才能执行下去。

Q1:怎么算到达屏障?
线程主动调用await()方法

Q2:如何执行线程数?
构造方法里指定

2.1 CycliBarrier的使用

//首先需要构造一个对象,指定线程总数
//这里指定了参数3,只有3个线程调用了对象c的await()方法后,3个线程才会执行下去。
CyclicBarrier c = new CyclicBarrier(3);

//在线程里使用。最后一个线程调用了,所有线程被唤醒,进入运行态
c.await()

2.2 使用CycliBarrier实现主线程等待两个线程执行完再执行

public class CycliBarrierDemo {
    static CyclicBarrier c = new CyclicBarrier(3);

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("线程1开始执行!");
                    Thread.sleep(3000);
                    System.out.println("线程1结束执行!");
                    c.await();
                } catch (Exception e) {

                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("线程2开始执行!");
                    Thread.sleep(5000);
                    System.out.println("线程2结束执行!");
                    c.await();
                } catch (Exception e) {

                }
            }
        });
        t1.start();
        t2.start();

        System.out.println("开始等待线程1、2结束");
        c.await();
        System.out.println("线程1、2结束");
    }
}

执行结果同1.2

2.3其它

CyclicBarrier还提供了另一个更高级的构造方法CyclicBarrier(int parties,Runnable barrierAction),可以在所有线程到达屏障后优先执行指定的线程的run方法。

  • Q1:这里执行Runnable是新启动一个线程吗?or 直接调用run方法执行?

三.CyclicBarrier和CountDownLatch的区别

CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。

CB还提供了getNumberWating方法获取阻塞的线程数量。isBroken方法用来了解阻塞的线程是否被中断。一个线程被中断后其他线程的await会抛出异常,这时调用isBroken方法可以输出是否有线程被中断。
另外调用线程的interrupt方法可以中断线程。

四.控制并发线程数的Semaphore

Semaphore(信号量)是用来控制同时访问特定资源的线程数量。

4.1基本使用

Semaphore 的构造方法需要传入一个参数,如 new Semaphore(10),
表示最多有10个线程可以获取到信号量,10个之后的线程再尝试获取时就会被阻塞。
获取信号量使用:s.acquire()方法;
释放信号量使用:s.release()方法。
只有前边的线程释放掉后,后面的线程(10个之后)才能被唤醒,重新获取信号量。

它可以用来控制同时 运行的线程的数目。

4.2扩展API

intavailablePermits(); //返回此信号量中当前可用的许可证数
intgetQueueLength();//返回正在等待获取许可证的线程数
booleanhasQueuedThreads();//是否有线程正在等待获取许可证
void reducePermits(int reduction); //减少reduction个许可证,是个protected方法
Collection getQueuedThreads(); //返回所有等待获取许可证的线程集合,是个protected方法

五.线程间交换数据的Exchanger

Exchanger可以用于线程间交换信息。它提供一个同步点,当两个线程都到达这个同步点时,它们的信息交换。只有一个到达时,它先等待,直到另一个线程也到达。

4.1基本使用

Exchanger e = new Exchanger();是一个泛型类,泛型指明交换数据的类型。
e.exchange("hello")开始等待另一个线程也调用同一个对象的exchange

具体例子:

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        final Exchanger exchanger = new Exchanger();

        threadPool.execute(new Runnable() {
            public void run() {
                try {
                    String b = exchanger.exchange("hello,anyone");
                    System.out.println("thread1 reviced :" + b);
                } catch (Exception e) {

                }
            }
        });

        threadPool.execute(new Runnable() {
            public void run() {
                try {
                    String a = exchanger.exchange("hello,anybody");
                    System.out.println("thread2 reviced :" + a);
                } catch (Exception e) {

                }
            }
        });

    }

Java并发之基础知识
Java并发之volatile关键字
Java并发之synchronized关键字
Java并发之原子类
Java并发之线程池
Java并发之并发工具类
Java并发之AQS原理
Java并发之ThreadLocal使用和源码分析


如果本文对您有帮助,欢迎关注我的原创微信公众号“Java技术小站”第一时间接收我的更多文章

Java并发之并发工具类_第1张图片
Java技术小站.png

你可能感兴趣的:(Java并发之并发工具类)