`
ol_beta
  • 浏览: 282470 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

java常用并发工具介绍

    博客分类:
  • java
阅读更多

 本文主要介绍的工具包括:

  • CountDownLatch

  • Semaphore

  • CyclicBarrier

  • Exchanger

CountDownLatch

CountDownLatch可以使一个或多个线程等待一组事件发生。在CountDownLatch内部维护一个计数器(被初始化为一个正整数),表示需要等待事件的数量。countDown()方法减少一个事件数量,await()将等待直到计数器为零的时候,才继续执行await后面的代码。如果计数器不为零,那么await将一直会阻塞等待直到计数器为零,或者阻赛线程中断/超时。

@Test
public void test() throws InterruptedException {
    // thread number
    final int threadNum = Runtime.getRuntime().availableProcessors() + 1;
    // start event
    final CountDownLatch startEvent = new CountDownLatch(1);
    // finish event
    final CountDownLatch finishEvent = new CountDownLatch(threadNum);

    for (int i = 0; i < threadNum; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // await for start
                    startEvent.await();
                    System.out.println(Thread.currentThread() + " start at : " + System.currentTimeMillis());
                    // current thread finish
                    finishEvent.countDown();
                } catch (InterruptedException ignore) {

                }
            }
        }).start();

        // sleep 0.5ms
        TimeUnit.MILLISECONDS.sleep(500);
    }

    long startTime = System.currentTimeMillis();

    startEvent.countDown();
    // wait for all thread finish
    finishEvent.await();
    System.out.println("total finish cost : " + (System.currentTimeMillis() - startTime) + "ms");

}

这个例子展示了如何在同一时间启动threadNum个线程,并且这threadNum个线程都完成后,记录执行结果。startEvent.await()将等待直到调用startEvent.countDown()。这是,所有线程在同一时间启动。当每个线程执行完毕的时候,会调用finishEvent.countDown()通知给主线程,finishEvent.await()将等待直到所有子线程都执行完毕。

打印结果:

Thread[Thread-1,5,main] start at : 1359782125125
Thread[Thread-8,5,main] start at : 1359782125125
Thread[Thread-7,5,main] start at : 1359782125125
Thread[Thread-6,5,main] start at : 1359782125125
Thread[Thread-5,5,main] start at : 1359782125125
Thread[Thread-3,5,main] start at : 1359782125125
Thread[Thread-0,5,main] start at : 1359782125125
Thread[Thread-2,5,main] start at : 1359782125125
Thread[Thread-4,5,main] start at : 1359782125125
total finish cost : 1ms

Semaphore

Semaphore在内部持有一个虚拟的许可组(初始化的时候可以设置虚拟组的数量),当执行某个操作的时候,调用acquire获得许可,在操作执行完成后调用release释放许可。如果没有许可可用,那么acquire方法会一直阻赛直到有许可可用为止,或者执行获取许可的线程终端或阻赛。

Semaphore可以用来控制某种资源的使用数量,或者同时使用特定资源的数量。利用这个特性,可以实现某种资源的资源池或者对容器实加边界。

@Test
public void test() throws InterruptedException {

    final BoundedList<Integer> list = new BoundedList<>(5);

    new Thread(new Runnable() {
        @Override
        public void run() {
            int index = 0;
            while (true) {
                try {
                    list.add(index++);
                    System.out.println(System.currentTimeMillis() + " add " + index);
                } catch (InterruptedException ignore) {

                }
            }
        }
    }).start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(new Runnable() {
        @Override
        public void run() {
            int index = 0;
            while (true) {
                try {
                    list.remove(index++);
                    System.out.println(System.currentTimeMillis() + " remove " + index);
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException ignore) {
                }
            }
        }
    }).start();
    Thread.currentThread().join();
}

static class BoundedList<T> {

    private final List<T> list;
    private final Semaphore semaphore;

    public BoundedList(int bound) {
        this.list = new ArrayList<>();
        semaphore = new Semaphore(bound);
    }

    public boolean add(T o) throws InterruptedException {
        semaphore.acquire();
        boolean added = false;
        try {
            added = list.add(o);
            return added;
        } finally {
            if (!added) {
                semaphore.release();
            }
        }
    }

    public boolean remove(T o) {
        boolean removed = list.remove(o);
        if (removed) {
            semaphore.release();
        }
        return removed;
    }
}        

这个例子展示了一个带边界的List,当向集合中添加元素的时候,首先获取许可。如果添加失败了,那么释放许可。当删除集合中的元素的时候,如果删除成功,释放一个许可。这样就能保证集合中的元素都是获得许可后才添加进来的,从而保证了集合的边界。

打印结果:

1359787233784 add 1
1359787233784 add 2
1359787233784 add 3
1359787233784 add 4
1359787233784 add 5
1359787234787 add 6
1359787234787 remove 1
1359787235288 remove 2
1359787235288 add 7
1359787235789 remove 3
1359787235789 add 8
1359787236290 remove 4
1359787236290 add 9
….

在这个例子中,生产者向集合中添加元素,消费者删除元素,因为生产者的速度大于消费者,所以当集合中元素等于5的时候,就必须等待消费者删除一个元素后才能再继续添加,从打印结果可以看出这点。

CyclicBarrier

CyclicBarrier和CountDownLatch有些类似,它阻塞一组线程直到某个事件发生。可以把CyclicBarrier理解成一个障碍,当所有线程都到达这个"障碍"的时候,才能继续下个事件。如果所有线程到达barrier处,barrier打开释放所有线程,并且barrier可以继续使用。如果await方法超时,或者被中断,那么认为barrier被打破,所有在await上阻塞的线程都将抛出BrokenBarrierException

@Test
public void test() throws InterruptedException {

    ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime()
            .availableProcessors() + 1);

    final int gate_threshold = 4;
    final CyclicBarrier gate = new CyclicBarrier(gate_threshold, new Runnable() {
        @Override
        public void run() {
            System.out.println("4 threads arrived, gate open...");
        }
    });

    while (true) {
        TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000));
        exec.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread() + " arrived");
                try {
                    gate.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

这个例子假设有一堆线程到达gate处,每当到达gate处的线程数达到gate_threshold时,gate打开释放这些线程并进入下次一次循环。

Exchanger

Exchanger提供两个线程以线程安全的形式交换数据,exchange等待另一个线程到达exchange方法,然后把数据给另一个线程并且接收另一个线程交换过来的数据。

@Test
public void test() throws InterruptedException {

    final int size =10;
    final Exchanger<List<Integer>> exchanger = new Exchanger<>();
    final List<Integer> emptyList = new ArrayList<>();
    final List<Integer> fullList = new ArrayList<>();

    Thread producer = new Thread(new Runnable() {
        @Override
        public void run() {
            List<Integer> currentList = emptyList;
            while (true){
               if(currentList.size()<size){
                   int num = (int)( Math.random() * 10000);
                   currentList.add(num);
                   System.out.println("producer : " + num);
                   try {
                       TimeUnit.MILLISECONDS.sleep((long) (Math.random()*1000));
                   } catch (InterruptedException ignore) {

                   }
               }else {
                   try {
                       System.out.println("producer : list full wait exchange");
                       currentList =  exchanger.exchange(currentList);
                       System.out.println("producer : exchanged");
                   } catch (InterruptedException ignore) {

                   }
               }
            }
        }
    });

    Thread consumer = new Thread(new Runnable() {
        @Override
        public void run() {
            List<Integer> currentList = fullList;
            while (true){
                if(currentList.size()!=0){
                    Integer remove = currentList.remove(0);
                    System.out.println("consumer : " + remove);
                    try {
                        TimeUnit.MILLISECONDS.sleep((long) (Math.random()*1000));
                    } catch (InterruptedException ignore) {
                    }
                }else {
                    try {
                        System.out.println("consumer : list empty wait exchange");
                        currentList =  exchanger.exchange(currentList);
                        System.out.println("consumer : exchanged");
                    } catch (InterruptedException ignore) {

                    }
                }
            }
        }
    });
    producer.start();
    consumer.start();
    producer.join();
    consumer.join();
}

这个例子展示了一个生产者/消费者的例子。当生产者填慢list后,等待交换。同样当消费者消耗完list,也等待交换。

 

分享到:
评论

相关推荐

    Java并发编程实战

    3.5.3 安全发布的常用模式 3.5.4 事实不可变对象 3.5.5 可变对象 3.5.6 安全地共享对象 第4章 对象的组合 4.1 设计线程安全的类 4.1.1 收集同步需求 4.1.2 依赖状态的操作 4.1.3 状态的所有权 4.2 实例...

    了解JAVA并发工具常用设计套路

    主要介绍了了解JAVA并发工具常用设计套路,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下

    Java 并发编程实战

    3.5.3 安全发布的常用模式 3.5.4 事实不可变对象 3.5.5 可变对象 3.5.6 安全地共享对象 第4章 对象的组合 4.1 设计线程安全的类 4.1.1 收集同步需求 4.1.2 依赖状态的操作 4.1.3 状态的所有权 4.2 实例...

    Java并发体系.png

    java并发体系-xmind图,高清。包含:并发基础、java内存模型、锁、常用并发工具类、线程池、atomic类等知识结构图。高清大图!

    Java并发编程实战经验总结-并发编程进阶必读

    适合人群: 适合有一定Java基础且想在并发领域有更深入研究的你。同样适合从业2-3年,一直只做业务增删改查想要继续...2.Java SDK常用并发工具类使用技巧及原理; 3.并发编程设计模式,助力你在工作中更好的运用并发。

    java经典面试题目-面经-java-Java语言的进阶概念-常用的库和框架-并发编程-网络编程-Web开发-面经

    什么是Java中的面向对象编程(OOP)?列举OOP的原则。...什么是Java中的并发编程?列举一些常见的并发类和工具。 什么是Java中的线程池?如何创建和使用线程池? 什么是Java中的Callable和Future接口?如何使

    java后端宝典进阶版.zip

    Java集合框架:介绍Java中常用的集合类,如List、Set、Map等,以及它们的特点、用法和性能分析,帮助读者选择合适的集合类来解决实际问题。 Java并发编程:深入讲解Java中的线程、锁、并发容器等并发编程相关的知识...

    Tools:Java中常用的工具

    工具 java编程中常用的工具 1)并发(ExecutorService) 2) 桂 (摇摆) 3) IO 保存和加载(文本文件)

    JAVA_API1.6文档(中文)

    java.util.concurrent 在并发编程中很常用的实用工具类。 java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类...

    java面试大全视频版

    Java面试题04.java中int占几个字节 Java面试题05.java面向对象的特征 Java面试题06.装箱和拆箱 Java面试题07.==和equals的区别 Java面试题08.String Java面试题09.讲一下java中的集合 Java面试题10.ArrayList 和...

    优雅的操作文件:java.nio.file 库介绍.pdf

    所以本章,我们就来主要介绍 java.nio.file 中常用的类和模块,大致如下: Path 路径:Paths 模块和 Path 工具类介绍 Files 文件:File 和 FileSystems 工具类介绍 文件管理服务:WatchService 、PathMatcher 等等...

    Java面试题合集最新版2024.zip

    Java面试通常涵盖多个...并发编程:了解Java中的线程、同步、锁等机制,以及Java并发包中的工具类。 JVM与性能调优:对Java虚拟机(JVM)有一定了解,包括内存管理、垃圾回收等方面,并知道如何进行基本的性能调优。

    毕业就业-刷题库Java面试题大全(2021年-2022年).rar

    a面试大全2021是一套最新Java面试必问合集,这本面试手册包含了Java基础、Java集合、JVM、Spring、Spring Boot、Spring Cloud、Mysql、Redis、RabbitMQ、Dubbo、Netty、分布式及架构设计等方面...8、并发工具 未完待续

    JAVA爬虫 并发爬取静态小说网站的全部小说.zip

    常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用...

    JUC多线程学习个人笔记

    JUC(Java Util Concurrent)是Java中用于并发编程的工具包,提供了一组接口和类,用于处理多线程和并发操作。JUC提供了一些常用的并发编程模式和工具,如线程池、并发集合、原子操作等。 JUC的主要特点包括: ...

    业级超高并发与高可用架构实现 JUC高并发编程 Java.Util.Concurrent源码+原理解析

    Java.Util.Concurrent是在并发编程中很常用的实用工具类。此包包括了几个小的、已标准化的可扩展框架,以及一些提供有用功能的类,没有这些类,这些功能会很难实现或实现起来冗长乏味。课程从技术原理和细节上,进行...

    Java 1.6 API 中文 New

    java.util.concurrent 在并发编程中很常用的实用工具类。 java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,...

    常用算法 Java 实现.zip

    Java是一种广泛使用的面向对象的编程语言,由Sun ...综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的角色,是现代软件开发不可或缺的重要工具之一。

    java源码包---java 源码 大量 实例

     WDSsoft的一款免费源代码 JCT 1.0,它是一个Java加密解密常用工具包。 Java局域网通信——飞鸽传书源代码 28个目标文件 内容索引:JAVA源码,媒体网络,飞鸽传书  Java局域网通信——飞鸽传书源代码,大家都知道VB...

Global site tag (gtag.js) - Google Analytics