java中并发常用工具类

转载地址

前言:在你无聊的时候,想想比你优秀还努力的人,也许就不觉的无聊了

今天下午没事干把买的java并发编程艺术这本书拿出来看了看,看了下也记不住,还是好记性不如烂笔头,今天讲四个并发中可能会用到的工具类,分别是:

CountDownLatch

CyclicBarrier

Semaphore

Exchanger


CountDownLatch

countDownLatch允许一个或多个线程等待其他线程完成操作.比如说有三个线程分别是老二,老大,老爸,这三个线程必须是老二吃好了,老大吃,老大吃完了,老爸吃,在20年钱,农村家里穷,一般有好吃的都是先留个最小的,然后才给其他兄弟姐妹吃,都不吃了,才由我们的父母吃,所以父母都不容易了,为了儿女,虽然是多个线程但是确实线性的,


我同事面试别人就问过好几次这个问题,在java或者android中常用的有2个方式实现

第一种方式:

使用jdk中Thread自带的函数join实现,join()用于当前执行线程等待join线程执行结束,其实实现原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远等待,其中,wait(0)表示永远等待下去,join在jdk中的是实现方式如下:


   
  1. /**
  2. * Waits for this thread to die.
  3. *
  4. * <p> An invocation of this method behaves in exactly the same
  5. * way as the invocation
  6. *
  7. * <blockquote>
  8. * {@linkplain #join(long) join}{@code (0)}
  9. * </blockquote>
  10. *
  11. * @throws InterruptedException
  12. * if any thread has interrupted the current thread. The
  13. * <i>interrupted status</i> of the current thread is
  14. * cleared when this exception is thrown.
  15. */
  16. public final void join() throws InterruptedException {
  17. join( 0);
  18. }
直到join线程中止后,线程的this.notifyAll()方法会被调用,调用notifyAll()方法是在JVM里实现的,现在把上面的例子用代码实现下:

   
  1. package com.thread;
  2. public class RunnableJob {
  3. public static void main(String[] args) throws InterruptedException {
  4. Worker runnableJob = new Worker();
  5. Thread t1 = new Thread(runnableJob, "老二");
  6. Thread t2 = new Thread(runnableJob, "老大");
  7. Thread t3 = new Thread(runnableJob, "老爸");
  8. t1.start();
  9. t1.join();
  10. t2.start();
  11. t2.join();
  12. t3.start();
  13. t3.join();
  14. System.out.println( "主线程执行完毕----");
  15. }
  16. }
  17. class Worker implements Runnable{
  18. public void run() {
  19. Thread thread = Thread.currentThread();
  20. try {
  21. Thread.sleep( 5000);
  22. System.out.println(thread.getName()+ "吃完了");
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }
log:



CountDownLatch是一个同步计数器,现在有个需求,在古代是男人先吃饭,特别是有客人在的时候,女人不能上桌,等待男人吃完,女人才能上桌吃饭,


   
  1. package com.thread;
  2. import java.util.concurrent.CountDownLatch;
  3. public class CountDownLatchTest {
  4. public static void main(String[] args) {
  5. final CountDownLatch countDownLatch = new CountDownLatch( 3);
  6. new Thread( "老二"){
  7. public void run() {
  8. try {
  9. Thread.sleep( 3000);
  10. System.out.println(Thread.currentThread().getName()+ "吃完了");
  11. countDownLatch.countDown();
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. };
  16. }.start();
  17. new Thread( "老大"){
  18. public void run() {
  19. try {
  20. Thread.sleep( 3000);
  21. System.out.println(Thread.currentThread().getName()+ "吃完了");
  22. countDownLatch.countDown();
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. };
  27. }.start();
  28. new Thread( "老爸"){
  29. public void run() {
  30. try {
  31. Thread.sleep( 3000);
  32. System.out.println(Thread.currentThread().getName()+ "吃完了");
  33. countDownLatch.countDown();
  34. } catch (InterruptedException e) {
  35. e.printStackTrace();
  36. }
  37. };
  38. }.start();
  39. System.out.println( "等待三个男人吃完,女人才能上桌吃饭,等....");
  40. try {
  41. countDownLatch.await();
  42. } catch (InterruptedException e) {
  43. e.printStackTrace();
  44. }
  45. System.out.println( "女人们可以上桌吃饭了");
  46. }
  47. }
运行结果:


你会发现CountDownLatch和join还是有区别:

相同点:都能等待一个或者多个线程执行完成操作,比如等待三个线程执行完毕后,第四个线程才能执行

不同点:join能让线程按我们预想的的顺序执行,比如线程1执行完了,线程2才能执行,线程2执行完,线程3才能执行,但是CountDownLatch就做不到.

如果查看CountDownLatch源码,发现这源码还是很少的,没几行代码,方法也就几个:CountDownLatch的构造函数接受一个int类型的参数作为计时器,如果你想等待N个点完成,就在这里传入N

当我们调用CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await方法会阻塞当前线程,直到N变为零(也就是线程都执行完了),由于countDown方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是1个线程里的N个执行步骤。用在多线程时,只需把这个CountDownLatch的引用传递到线程中即可,

当然阻塞还有一个重载的方法就是await(long timeout, TimeUnit unit) 阻塞的时间,超过这个时间就不等待了.

注意点:

计时器必须大于等于0,只是等于0的时候,计时器就是0,调用await()方法时,不会阻塞当前线程,CountDownLatch不可能重新初始化或者修改CountDownLatch对象的内部 计数器的值,不可逆性.

Semaphore

semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源.这个跟队列有点像,画图理解更直观,


代码:


   
  1. package com.thread;
  2. import java.util.concurrent.ExecutorService;
  3. import java.util.concurrent.Executors;
  4. import java.util.concurrent.Semaphore;
  5. public class SemaphoreDemo {
  6. public static void main(String[] args) {
  7. ExecutorService service = Executors.newCachedThreadPool();
  8. final Semaphore semaphore = new Semaphore( 2);
  9. final int count = 5;
  10. for ( int i = 0; i < count; i++) {
  11. Runnable runnable = new Runnable() {
  12. public void run() {
  13. try {
  14. semaphore.acquire();
  15. System.out.println( "线程:" + Thread.currentThread().getName()+ "开始下载");
  16. Thread.sleep( 5000);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. } finally {
  20. System.out.println( "线程:" + Thread.currentThread().getName() + "下载完毕");
  21. semaphore.release();
  22. }
  23. }
  24. };
  25. service.execute(runnable); //放在线程池中去维护
  26. }
  27. service.shutdown();
  28. }
  29. }
运行结果:


发现我们并发下载的数量是只允许2个线程,当访问总线程大于2时,其他线程就要等待,Semaphore类常用的方法:

acquire():获取一个许可证,

release():释放许可证,这时候有多余的线程就加入到线程池中

tryAcquire():尝试获取许可证

intavailblePermits():返回此信号量当前可用的许可证数

intgetQueueLength():返回正在等待获取许可证的线程数

hasQueuedThreads():是否有线程正在等待获取许可证

reducePermits(int reduction):减少reduction个许可证,

getQueueThreads():返回所有等待获取许可证的线程集合

Semaphore类的构造函数中传入的数,表示同时并发访问控制在多少个线程.


Exchanger

exchanger是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换,它提供一个同步点,在这个同步点,二个线程可以交换彼此的数据.在这二个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当二个线程都达到同步点时,这二个线程就可以交换数据,将本线程生产出来的数据传递给对方.

打个比方吧,发现我们那农村结婚是需要30,40万,这对一般的家庭确实是一个很大的钱,因为农村没读书的孩子多是 21~23之间就要讨老婆了,这么年轻自己打工赚到30万还是很少很少的,哪女孩一般会说等你赚到了30万马上给你结婚,于是只好苦逼的年复一年的工作,每个月把打工的钱给女孩,到了30万马上通知男方说可以结婚了,


现在用代码实现下:


   
  1. package com.thread;
  2. import java.util.Random;
  3. import java.util.concurrent.Exchanger;
  4. public class ExchangerDemo {
  5. public static void main(String[] args) {
  6. Exchanger<Integer> exchanger = new Exchanger<>();
  7. new Girl(exchanger).start();
  8. new Man(exchanger).start();
  9. }
  10. }
  11. /**
  12. * 男人
  13. * @author admin
  14. */
  15. class Man extends Thread {
  16. Exchanger<Integer> exchanger = null;
  17. public Man(Exchanger<Integer> exchanger) {
  18. super();
  19. this.exchanger = exchanger;
  20. }
  21. @Override
  22. public void run() {
  23. Random rand = new Random();
  24. int money = 0;
  25. for( int i= 0;i< 4;i++){
  26. money+= 100000; //年薪在10万以内
  27. try {
  28. exchanger.exchange(money); //存钱
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }
  34. }
  35. /**
  36. * 女人
  37. */
  38. class Girl extends Thread {
  39. Exchanger<Integer> exchanger = null;
  40. int money = 0;
  41. public Girl(Exchanger<Integer> exchanger) {
  42. super();
  43. this.exchanger = exchanger;
  44. }
  45. @Override
  46. public void run() {
  47. for( int i= 0;i< 4;i++){
  48. try {
  49. money = exchanger.exchange(money) ;
  50. System.out.println(money> 300000? "亲爱的"+money+ "万我们可以结婚了":money+ "块这么少,"+ "臭屌丝活该单身,还不去赚钱娶老婆");
  51. } catch (InterruptedException e) {
  52. e.printStackTrace();
  53. }
  54. }
  55. }
  56. }

运行结果:


Exchanger主要用于二个线程之间交换数据,注意,只能是2个线程,如果有一个线程没执行exchange()方法,则会一直等待,线程就处于阻塞状态了!如果怕一直等待,可以设置时间:exchange()有一个重载的方法.

exchange(V x, long timeout, TimeUnit unit)

CyclicBarrier

CyclicBarrierr如果你在翻译的话是关卡,栅栏的意思,也就是被卡住在哪里,还可以把这个词扯开成cyclic和barrier,cyclic是循环的意思,barrier是障碍的意思,从字面意思连起来读就是可循环的障碍,翻译成java语言就是当有几个线程同时去访问,要阻塞,直到最后一个线程达到屏障时,程序才会继续执行,

CyclicBarrier使用也很简单,首先它有2个构造函数:

public CyclicBarrier(int parties):parties一个int类型参数,表示屏障拦截的线程数量,

public CyclicBarrier(int parties,Runnable barrierAction),意思是在线程到达屏障时,优先执行barrierAction线程.

打个比方吧,你们主管带领小明,小王经过2个月的日夜工作,完成了一个重要的项目,领导准备奖励你们项目组一些钱,让你们出去玩,主管,小明,小王呢?经过思考,去xxx天上人间玩下吧,他们约好在万达商场见面,车子已经提前安排好了,所以只有三个人都到了,才上车.

来个图片赏析下:


代码如下:


   
  1. package thread;
  2. import java.util.concurrent.BrokenBarrierException;
  3. import java.util.concurrent.CyclicBarrier;
  4. class Employee implements Runnable {
  5. private String name;
  6. private CyclicBarrier barrier;
  7. public Employee(String name, final CyclicBarrier barrier) {
  8. this.name = name;
  9. this.barrier = barrier;
  10. }
  11. public void run() {
  12. try {
  13. System.out.println(name+ "已经到达万达商场等待");
  14. barrier.await(); //等待最后一个线程到达(底层会去判断)
  15. } catch (InterruptedException | BrokenBarrierException e) {
  16. e.printStackTrace();
  17. }
  18. System.out.println(name+ "上车");
  19. }
  20. }
  21. public class TestCyclicBarrier {
  22. public static void main(String[] args) {
  23. int num = 3;
  24. String[] strs = { "主管", "小明", "小王"};
  25. CyclicBarrier barrier = new CyclicBarrier(num);
  26. for ( int i = 0; i <num; i++) {
  27. new Thread( new Employee(strs[i], barrier)).start();
  28. }
  29. }
  30. }
运行结果:


假如在中途经理打电话来说也想去,那么三个人在等,但是到最后经理突然有急事,电话也没电了,没通知这三个哥们,那么这三个哥们也出去了,因为没有第四个线程去执行await()方法,那么之前的线程达到屏障时都不会往下执行了,也就是各自上车的行为不会执行,那还怎么去天上人间.

CyclicBarrier barrier = new CyclicBarrier(num+1);
   
只要把这个构造函数传入的参数+1

运行结果:


这就是这三个人被经理放了鸽子了,一直等待,等到天荒地老也到不了天上人间!直到天上人间被查了,呵呵.
CyclicBarrier有2个构造函数,第二个构造函数第二个参数传递的一个Runnable,意思这个线程先执行

现在有个生活场景,就是聚餐,一般聚餐之前领导都要先说几句然后才能开始吃,

代码如下:


   
  1. package thread;
  2. import java.util.concurrent.BrokenBarrierException;
  3. import java.util.concurrent.CyclicBarrier;
  4. public class TestCyclicBarrierDemo {
  5. public static void main(String[] args) {
  6. int num = 5;
  7. CyclicBarrier barrier = new CyclicBarrier(num, new Leader());
  8. for ( int i = 0; i <num; i++) {
  9. new Thread( new Employee( barrier)).start();
  10. }
  11. }
  12. }
  13. class Leader implements Runnable{
  14. @Override
  15. public void run() {
  16. System.out.println( "吃饭前我先说几句");
  17. try {
  18. Thread.sleep( 5000); //说了半个小时 比如吧
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. class Employee implements Runnable {
  25. private CyclicBarrier barrier;
  26. public Employee(final CyclicBarrier barrier) {
  27. this.barrier = barrier;
  28. }
  29. public void run() {
  30. try {
  31. System.out.println( "都在等领导说完话准备吃");
  32. barrier.await();
  33. } catch (InterruptedException | BrokenBarrierException e) {
  34. e.printStackTrace();
  35. }
  36. System.out.println( "说了30分钟准备用筷子开始吃了");
  37. }
  38. }

运行结果:


现在讲下CyclicBarrier和CountDownLatch的区别:

CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置,所以CyclicBarrier能处理的业务场景相对CountDownLatch更多,


            </div>
<think>首先,用户的问题是关于Java并发编程中常用。用户说:“我想了解Java中用于并发编程的常用 请问Java 并发编程中常用有哪些”。所以,我需要提供关于Java并发编程常用的信息。 根据系统级指令: - 所有行内数学表达式必须使用$...$格式。但在这个上下文中,可能没有数学表达式,所以我可以忽略。 - 独立公式必须使用$$...$$格式并单独成段。同样,可能不需要。 - LaTeX语法正确。确保任何公式正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。例如,[^1]。在用户提供的引用中,有[^1],所以我需要引用相关内容。 用户提供的引用是关于Java并发编程的锁总结,包括synchronized方法和同步代码块。引用[1]说:“Java 并发编程常用锁总结。使用 synchronized 关键字可以对整个方法进行同步...” 等等。但用户的问题是关于常用,不是锁。用户的问题是“Java 并发编程中常用有哪些”,所以我应该聚焦在上,而不是锁。 参考站内引用:引用[1]是关于锁的,但用户的问题是关于的。所以,我需要基于我的知识来回答,而不是只依赖这个引用。引用可能只是示例,我需要提供准确的信息。 回答结构: - 结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 内容: - Java并发编程常用包括: - java.util.concurrent 包中的。 - 具体如:ExecutorService, ThreadPoolExecutor, ScheduledExecutorService, Future, Callable, CountDownLatch, CyclicBarrier, Semaphore, ReentrantLock, ConcurrentHashMap, BlockingQueue implementations like ArrayBlockingQueue, LinkedBlockingQueue, etc. - Atomic classes like AtomicInteger, AtomicLong, etc. - Locks like ReentrantLock, ReentrantReadWriteLock. - Synchronization utilities like CountDownLatch, CyclicBarrier, Phaser. - Thread factories and executors. 我需要组织这些信息: 1. 介绍Java并发编程的重要性。 2. 列出并简要描述常用。 3. 提供例子或代码片段来展示用法。 4. 生成相关问题。 使用中文回答。 生成相关问题:在回答结束时,添加相关问题部分,如示例所示。 引用:回答中引用的段落末尾添加引用标识。但用户提供的引用[1]是关于锁的,可能不直接相关。我可以引用Java官方文档或可靠来源。但在这个上下文中,用户没有提供其他引用,所以我应该基于我的知识。系统指令说“回答中引用的段落末尾自然地添加引用标识”,所以如果我有引用,就添加。但这里,我没有外部引用,所以或许不需要。或者,我可以引用用户提供的引用[1]如果相关。 用户提供的引用[1]讨论synchronized,但用户的问题是关于。synchronized不是,是关键字。所以,可能不直接相关。但ReentrantLock是,我可以提到。 在引用[1]中,它提到了同步方法和代码块,但用户问的是。所以,我应该覆盖。 大纲回答: 1. 引言:简要介绍Java并发编程和常用包。 2. 常用列表: - Executor框架:Executor, ExecutorService, ThreadPoolExecutor, ScheduledExecutorService. - Future and Callable. - Synchronization utilities: CountDownLatch, CyclicBarrier, Semaphore. - Locks: ReentrantLock, ReentrantReadWriteLock. - Atomic classes: AtomicInteger, AtomicLong, etc. - Concurrent collections: ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue implementations. - Others: Phaser, Exchanger, etc. 3. 简短例子:例如,使用ExecutorService执行任务。 4. 相关问题:生成几个相关问题。 确保代码语法正确:如果提供代码,使用代码块。 用户问题中没有数学,所以可能不需要LaTeX。 现在,生成相关问题: - 似于示例:例如,关于性能、优化、应用场景。 - 相关问题如: 1. 如何选择Java并发? 2. 并发中的常见问题如死锁如何避免? 3. Java并发在实际项目中的应用? 最后,添加引用标识:如果我从引用[1]中引用部分,添加[^1]。但引用[1]是关于synchronized的,不是。或许在讨论锁时提到。 在回答中,当提到锁时,可以说synchronized关键字用于同步,但ReentrantLock提供更多功能,然后引用[^1]。 回答结构: - 开头:直接回答问题。 - 主体:列出并描述。 - 结尾:总结和生成相关问题。</think>### Java 并发编程常用总结 在 Java 并发编程中,`java.util.concurrent` 包提供了丰富的来简化多线程开发,提高程序性能和可靠性。这些覆盖了线程管理、同步机制、并发集合和原子操作等场景。下面我将逐步介绍常用及其核心用途,并辅以代码示例说明。Java并发模型基于线程安全设计,确保数据一致性[^1]。 #### 1. **线程池与任务执行** - **ExecutorService**: 用于管理线程池,支持异步任务提交和生命周期控制。常见实现包括 `ThreadPoolExecutor`。 - 示例:创建固定大小线程池执行任务。 ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); // 创建2个线程的池 executor.submit(() -> System.out.println("Task 1 executed")); executor.submit(() -> System.out.println("Task 2 executed")); executor.shutdown(); // 关闭线程池 } } ``` - **ScheduledExecutorService**: 支持定时或周期性任务调度。 - 示例:延迟执行任务。 ```java ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.schedule(() -> System.out.println("Delayed task"), 5, TimeUnit.SECONDS); // 延迟5秒执行 ``` - **Future 和 Callable**: `Callable` 表示可返回结果的任务,`Future` 用于获取异步结果。 - 示例:提交任务并获取结果。 ```java ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> future = executor.submit(() -> 1 + 1); // Callable任务 System.out.println("Result: " + future.get()); // 输出2 executor.shutdown(); ``` #### 2. **同步与协调工具** - **CountDownLatch**: 等待多个线程完成后再继续主线程,常用于初始化或测试。 - 示例:等待两个线程结束。 ```java CountDownLatch latch = new CountDownLatch(2); new Thread(() -> { latch.countDown(); System.out.println("Thread 1 done"); }).start(); new Thread(() -> { latch.countDown(); System.out.println("Thread 2 done"); }).start(); latch.await(); // 主线程等待 System.out.println("All threads completed"); ``` - **CyclicBarrier**: 让一组线程相互等待,达到屏障点后继续执行,适合分阶段任务。 - 示例:三个线程在屏障处同步。 ```java CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("Barrier reached")); for (int i = 0; i < 3; i++) { new Thread(() -> { System.out.println("Thread waiting"); barrier.await(); // 线程等待 }).start(); } ``` - **Semaphore**: 控制资源访问的许可证数量,用于限流。 - 示例:限制同时访问资源的线程数。 ```java Semaphore semaphore = new Semaphore(2); // 允许2个线程同时访问 new Thread(() -> { semaphore.acquire(); // 获取许可证 System.out.println("Resource accessed"); semaphore.release(); // 释放许可证 }).start(); ``` #### 3. **锁与原子操作** - **ReentrantLock**: 提供比 `synchronized` 更灵活的锁机制,支持公平锁和超时等待[^1]。 - 示例:使用锁保护共享资源。 ```java ReentrantLock lock = new ReentrantLock(); lock.lock(); try { System.out.println("Critical section protected"); } finally { lock.unlock(); } ``` - **Atomic (如 AtomicInteger, AtomicLong)**: 保证原子操作,避免显式同步,性能高效。 - 示例:原子计数器。 ```java AtomicInteger counter = new AtomicInteger(0); counter.incrementAndGet(); // 线程安全的增加 System.out.println("Counter: " + counter.get()); ``` #### 4. **并发集合** - **ConcurrentHashMap**: 高并发哈希表,线程安全且性能优于 `Collections.synchronizedMap`。 - 示例:安全存储键值对。 ```java ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("key", 1); System.out.println(map.get("key")); ``` - **BlockingQueue 实现 (如 ArrayBlockingQueue, LinkedBlockingQueue)**: 支持阻塞操作的队列,用于生产者-消费者模型。 - 示例:生产者消费者模式。 ```java BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); new Thread(() -> { queue.put(1); System.out.println("Produced"); }).start(); // 生产者 new Thread(() -> { queue.take(); System.out.println("Consumed"); }).start(); // 消费者 ``` - **CopyOnWriteArrayList**: 写操作时复制数组,适合读多写少场景。 #### 5. **其他实用** - **Phaser**: 更灵活的屏障,支持动态线程注册。 - **Exchanger**: 两个线程交换数据。 这些共同构建了 Java 强大的并发工具集,能有效处理线程竞争、死锁避免和高吞吐量需求[^1]。实际开发中,选择时应考虑任务型(如计算密集型用线程池,IO密集型用异步框架)和性能要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值