美文网首页Java并发专题
Java并发之并发工具类

Java并发之并发工具类

作者: 第四单元 | 来源:发表于2018-12-18 22:49 被阅读0次

    在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<String> e = new Exchanger<String>();是一个泛型类,泛型指明交换数据的类型。
    e.exchange("hello")开始等待另一个线程也调用同一个对象的exchange

    具体例子:

        public static void main(String[] args) {
            ExecutorService threadPool = Executors.newFixedThreadPool(2);
            final Exchanger<String> exchanger = new Exchanger<String>();
    
            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并发之并发工具类

        本文链接:https://www.haomeiwen.com/subject/kdurkqtx.html