美文网首页multiThread
java中并发常用工具类

java中并发常用工具类

作者: huanfuan | 来源:发表于2018-07-23 16:26 被阅读212次

    《java并发编程的艺术》

    在JDK的并发包里提供了几个非常有用的并发工具类。

    1.CountDownLatch

    CountDownLatch,类似于计数器的方式,用于等待一个或多个线程执行完操作开始自身代码的执行。

    其构造函数接收一个int类型的整数作为计数器而使用,例如如果想等待N个线程执行完毕就传入N,当每调用一次countDown函数,表示某一个线程执行完毕。实际上,这个N并不是与线程绑定,也就是说并不是一定和线程的数量一致,只需要countDown函数执行N次,当前等待的线程就会开始执行。下面列出具体代码:

    
    public class CountDownLatchTest {
     
        static CountDownLatch countDownLatch = new CountDownLatch(2);
     
     
        public static void main(String []args) {
     
            new Thread(new Runnable() {
                @Override
                public void run() {
     
                    SleepUtils.second(2);
                    System.out.println("1");
                    countDownLatch.countDown();
     
                }
            }).start();
     
            new Thread(new Runnable() {
                @Override
                public void run() {
     
                    SleepUtils.second(4);
                    System.out.println("2");
                    countDownLatch.countDown();
                }
            }).start();
     
            try {
                // 主线程开始等待
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
     
            System.out.println("3");
     
        }
     
    }
    

    输出入下:

    
    1
    2
    3
    Process finished with exit code 0
    
    

    注意:

    1、如果传入的参数大于2,那么主线程将会一直等待。

    2、计数器必须大于0,如果为0,调用await方法不会阻塞当前线程。

    应用场景:

    当遇到一个比较耗时的计算量较大的任务时,我们则可以考虑使用多线程来操作,将一个大任务拆分成多个小任务(一个任务相当于一个线程),当每个小任务执行完毕返回结果后,再由某一主线程对结果进行统计。

    2.CyclicBarrier

    CyclicBarrier即同步屏障,它主要功能是让一组线程达到一个屏障(也可以称为同步点)是被阻塞,直到最后一个线程达到屏障是,屏障才被打开,所有被拦截的线程才会继续执行。

    其构造函数默认也是接收一个int类型的参数N作为屏障拦截的线程数量,每个线程调用await方法表示到达了屏障点,然后被阻塞。具体示例如下:

    public class CyclicBarrierTest {
     
     
        // 参数表示屏障拦截的线程数量, 每个线程调用 await方法,告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞
        // 屏障拦截的线程数量必须和当前的线程数一致,并且都调用await方法,否则当前所有的线程都处于等待状态
        static CyclicBarrier c = new CyclicBarrier(3);
     
        public static void main(String []args){
     
            new Thread(new Runnable() {
                @Override
                public void run() {
     
                    try {
                        System.out.println("1---1   "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        c.await();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
     
     
                    System.out.println("1---2   "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }
            }).start();
     
     
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("2---1   "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    try {
                        c.await();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
     
                    System.out.println("2---2   "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }
            }).start();
     
     
            SleepUtils.second(2);
            System.out.println("3---1   "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
            try {
                c.await();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
     
            System.out.println("3---2   "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
     
     
     
        }
     
    }
    
    

    输出如下:

    1---1   17:05:01
    2---1   17:05:01
    3---1   17:05:03
    3---2   17:05:03
    1---2   17:05:03
    2---2   17:05:03
    Process finished with exit code 0
    
    

    注意点:

    1、构造函数中的N必须为线程的总数,当最后一个线程调用await方法(到达屏障)时,屏障才会打开,被阻塞的线程才会执行,这里的N表示的含义和CountDownLatch传入的N是不一样的。

    2、我们发现,当所有线程都到达屏障时,当屏障打开,接下来会优先执行哪个线程呢?如上代码答案是不确定的。但是CyclicBarrier为我们提供了一个更高级的用法,即构造函数还支持传递一个Runnable对象,当屏障打开时,优先执行Runnable中的run方法。(这一功能十分强大,完全可以替代CountDownLatch了)

    应用场景:

    同CountDownLatch

    与CountDownLatch区别:

    1、CountDownLatch计数器只能使用一次,而CyclicBarrier的计数器可以使用 reset方法重置,所以适合更复杂的业务场景。

    3.Semaphore

    Semaphore即信号量,主要用来控制并发访问特定资源的线程数量,协调各个线程合理使用公共资源。

    构造函数同样也是接收一个int类型的参数N作为入参,用来限制访问某一公共资源最大的线程并发数,通过acquire来获取许可证,release释放许可证。

    具体示例如下:

    public class SemaphoreTest {
     
        private static final int THREAD_COUNT = 6;
        private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
        private static Semaphore semaphore = new Semaphore(2);
     
        public static void main(String []args){
     
     
            for(int i=0; i<THREAD_COUNT; i++){
                threadPool.execute(new MyRunnable(i+1));
            }
     
            threadPool.shutdown();
     
        }
     
     
        static class MyRunnable implements Runnable{
     
            private int sleep;
     
            public MyRunnable(int sleep) {
                this.sleep = sleep;
            }
     
     
            @Override
            public void run() {
                try {
                    semaphore.acquire();
     
                    System.out.println("save data   "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    SleepUtils.second(sleep);
     
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
     
    }
    
    

    输出如下:

    save data   19:44:37
    save data   19:44:37
    save data   19:44:38
    save data   19:44:39
    save data   19:44:41
    save data   19:44:43
    Process finished with exit code 0
    
    

    注意点:

    1、通过输出可以发现,线程的并发数量为2,当有一个线程执行完后则下一个线程才获取到资源。

    应用场景:

    我们有大量的线程在完成一个巨量任务的时候,但是某一公共资源却有限定了线程的链接树,这时候就需要对这些大量线程访问这一公共资源做控制。例如当我们有上百个线程需要处理本地上G的数据文件,每个线程处理完成之后需要把结果写到数据库,而数据库只支持十个线程的并发链接,此时,对数据库的链接我们就可以通过Semaphore来控制最大连接数。

    4.Exchanger

    Exchanger(交换者),它是用于线程间的协作工具类,主要用于线程间数据的交换。它提供了一个同步点,在这个同步点,两个线程可以交换彼此的数据。下面看具体demo:

    public class ExchangerTest {
     
        private static final Exchanger<String> exchanger = new Exchanger<>();
        private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
     
        public static void main(String[] args){
     
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    String a = "aaaaaaaaaa";
                    try {
                        String b = exchanger.exchange(a);
                        System.out.println("---"+b);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
     
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        String b = "bbbbbbbb";
                        String a = exchanger.exchange("bababa");
                        System.out.println("a is "+a+" , b is "+b);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
     
                }
            });
     
     
            threadPool.shutdown();
     
        }
     
    }
    
    

    输入如下:

    ---bababa
    a is aaaaaaaaaa , b is bbbbbbbb
     
    Process finished with exit code 0
    
    

    注意点:

    1、Exchanger只能作用于两个线程之间,如果作用于第三个线程,则第三个线程一直处于等待中;

    2、exchange中还有一个重载函数,接收一个等待时长,用于避免一直等待。

    最后总结:

    1、CountDownLatch,它是一种计数器的方式保证线程同步;它不去控制多个子线程之间的前后关系,只保证某一线程能够在这些子线程执行完成后再执行。

    2、CyclicBarrier,通过设置屏障的方式使得多线程同步,能够控制多个线程在屏障处等等其他线程也执行到屏障点,可以实现CountDownLatch具有的功能,但是比CountDownLatch功能强大;

    3、Semaphore,信号量,用于控制访问某一公共资源的并发线程数;

    4、Exchanger,用于两个线程之间的数据交换。

    参考链接:《java并发编程的艺术》

    文章链接:https://blog.csdn.net/coderinchina/article/details/54914852
    https://blog.csdn.net/u010349644/article/details/81013467

    相关文章

      网友评论

        本文标题:java中并发常用工具类

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