`
clearity
  • 浏览: 36906 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

获得更好并发和线程安全性的场景和解决方案--第二部分 CountDownLatch(闭锁) and CyclicBarrier(循环关卡)

 
阅读更多

现实场景下我们可以借助java.util.concurrent包下的以下几个类做到更好。

    1.CountDownLatch(闭锁)

    2.CyclicBarrier(关卡)

以下有一些具体的现实场景可以使以下的类派上用场。

 

场景:主线程创建三个数据库连接,并且在主线程中为每个连接分配三个子线程。主线程必须等待子线程执行完成并且所有的数据库连接关闭后才能退出,所以,我们应该怎么去实现呢?

 

方案:因为我们已经知道了线程的具体数量,所以我们可以使用CountDownLatch(闭锁)。CountDownLatch可以在主线程中使用去实现等待子线程的功能。当前CountDownLatch可以使用数字3来初始化创建。

 

CountDownLatch countDownLatch = new CountDownLatch(MAX_THREADS);

 主线程生成若干子线程并且通过await方法等待count变成0的时候才开始执行

countDownLatch.await();

 

在每个子线程run()方法执行的过程时,只要子线程完成处理操作,count数就会递减

 

countDownLatch.countDown(); 

 



 

 

图上所示的是当若干工作线程使用startSignal(闭锁)等待其他工作线程来启动并且主线程通过stopSignal(闭锁)方法等待所有的工作线程

 

import java.util.concurrent.CountDownLatch;
 
public class Worker implements Runnable {
  
    private CountDownLatch startLatch;
    private CountDownLatch stopLatch;
 
    public Worker(CountDownLatch startLatch, CountDownLatch stopLatch) {
       this.startLatch = startLatch;
       this.stopLatch = stopLatch;
    }
 
    @Override
    public void run() {
        try {
            startLatch.await(); // 等待闭锁递减到0
            System.out.println("Running: " + Thread.currentThread().getName());
        } catch (InterruptedException ex)  {
            ex.printStackTrace();
        }
        finally {
         //闭锁递减到0,主线程继续执行
         stopLatch.countDown(); 
        }
 }
 
}

 

最后,定义创建工作线程的WaitForAllThreadsToStart类

 

import java.util.concurrent.CountDownLatch;
 
public class WaitForAllThreadsToStart {
  
 private static final int MAX_THREADS = 3;
  
 public static void main(String[] args) throws Exception {
     CountDownLatch startSignal = new CountDownLatch(1);   //闭锁递减从1到0
     CountDownLatch stopSignal = new CountDownLatch(MAX_THREADS); // 闭锁递减从3到0
         
     System.out.println("The main thread is going to spawn " + MAX_THREADS  + " worker threads.....");  
     for (int i = 1; i <= MAX_THREADS; i++) {
        Thread t = new Thread(new Worker(startSignal,stopSignal), "thread-" + i);
        Thread.sleep(300);
        t.start();
        System.out.println("Started: " + t.getName() + " but waits for other threads to start.");
     }
         
     //等待线程在闭锁从1递减到0后开始继续执行 
     startSignal.countDown();
     System.out.println("worker threads can now start executing as all worker threads have started.....");
     try{       
        stopSignal.await(); // 等待工作线程操作使闭锁递减到0
     } catch (InterruptedException ex){
        ex.printStackTrace();
     }
     System.out.println("finished executing the worker threads and the main thread is continuing.");
     System.out.println("The main thread can execute any task here.");
         
 }
}

 

输出如下:

 

The main thread is going to spawn 3 worker threads.....
Started: thread-1 but waiting for other threads to start.
Started: thread-2 but waiting for other threads to start.
Started: thread-3 but waiting for other threads to start.
worker threads can now start executing as all worker threads have started.....
Running: thread-1
Running: thread-3
Running: thread-2
finished executing the worker threads and the main thread is continuing.
The main thread can execute any task here.

 

场景:如果在以上的场景下增加一个特殊的需求让三个子线程互相等待?举个例子,如果三个线程中的每个都需要完成两个任务。在线程开始执行第二个任务之前,所有的三个线程都必须完成第一个任务。第一个任务是从数据库读取数据并且第二个任务对数据进行计算,最后所有的计算结果需要被合并由一个线程写回数据库.

 

方案:关卡可以在当前操作前有若干子进程并且需要实现等待所有子进程处理完成的场景下使用.这在需要多个并行进程完成一个串行处理的情况下很有用处.可以使用的方法如cyclicBarrier.await()和cyclicBarrier.reset()方法



 

 

以下的代码可以帮助你理解.首先是WorkerTask线程

 

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
 
public class WorkerTask implements Runnable {
  
 private CyclicBarrier barrier;
   
 public WorkerTask(CyclicBarrier barrier) {
  this.barrier = barrier;
 }
 
 @Override
 public void run() {
   String threadName = Thread.currentThread().getName();
   try {
         System.out.println(threadName + " is now performing Task-A");
         barrier.await();      //等待所有线程完成任务A的关卡
            
         System.out.println(threadName + " is now performing Task-B");
         barrier.await();     //等待所有关卡完成任务B的关卡
            
   } catch (BrokenBarrierException ex)  {
         ex.printStackTrace();
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
  }
}

 

 

现在,测试类创建了执行任务A和任务B的工作线程,并且关卡线程完成合并处理

 

import java.util.concurrent.CyclicBarrier;
 
public class WaitForBarrierPoint {
  
 private static final int MAX_THREADS = 3;
 private static final int NO_OF_TASKS = 2;    //任务A和B
  
 private static int taskCount = 0;
  
 //创建一个监视关卡条件的线程,也就是等MAX_THREADS完成任务进行合并操作
//由匿名内部类来执行
 private static CyclicBarrier cb = new CyclicBarrier(MAX_THREADS, new Runnable() {
  
   @Override
   public void run() {
     System.out.println("All " + MAX_THREADS + " threads have reached the barrier point.");
     ++taskCount;
    
     //在所有任务完成后执行合并处理
     if(taskCount == NO_OF_TASKS) {
      System.out.println("The consolidation job can start now .... ");
   }
  }
 }); 
  
  
 public static void main(String[] args) {
  Thread t = null;
  //create 3 worker threads
  for (int i = 1; i <= MAX_THREADS; i++) {
   t = new Thread(new WorkerTask(cb), "Thread-" + i);
   t.start();
  }
   
  System.out.println("The main thread ends here.");
 }
  
}

 

 

Q.为什么称它为cyclic barrier(循环关卡)?

A.因为它的作用就是让若干线程等待彼此完成任务的关卡点.关卡被称为循环的原因是因为所有等待的工作线程被放行后在下个关卡点之前还可以被重用.

 

关卡点可以通过传递下面的参数给构造器来创建.

  1.参与并行操作的线程数目

  2.满足通过关卡条件的操作完成之后需要调用的处理

 

每步操作(或者迭代)时:

  每个线程完成属于自己工作的一部分来完成一步操作

  在完成了自己的工作部分后,线程会调用关卡的await方法

  await方法只有下列返回情况:

  1.三个线程都应调用过await()

  2.融合或者混合方法已经被执行(在对等待线程放行前,barrier已经在最后一个线程调用await()时调用过)

 

如果三个线程中的任何一个在等待barrier的时候被中断或者超时,这时关卡点已经被破坏掉,所以其他等待的线程会收到一个BrokenBarrierException的异常.这个异常会被传递到所有的线程并被其他步骤终止,或者只是被其中一个线程从外部中断掉.

 

Q.因此,什么时候使用CountDownLatch和什么时候可以使用CyclicBarrier呢?

A.CountDownLatch是通过一个初始数初始化的.线程可以等待闭锁的递减或者等待直到0.当最后到达0的时候,所有等待的线程都会被恢复.

 

如果你希望在一个点重复的执行一系列线程,你最好使用CyclicBarrier(关卡).举例来说,启动一批线程,在一个点会合,为了满足,比如一些如混合或者合并的工作,然后再重新会合,做一些验证,然后重复这些操作.

 

CountDownLatch只能被使用一次,在多步骤的操作中显得不太方便,如需要在多个阶段将不同的线程的中间结果进行合并.闭锁不应该显式的允许一个线程可以有通知其他线程停止等待的权利,虽然有时是有用的,比如如果线程之一的发生错误.

 

关卡在以下的场景下会比CountDownLatch(闭锁)更有用:

   1.多个阶段或者迭代的多线程操作

   2.多个阶段或者迭代的单线程操作中,例如,为了合并前面多线程阶段的结果而进行的处理

  • 大小: 64.6 KB
  • 大小: 25.1 KB
0
1
分享到:
评论

相关推荐

    CountDownLatch和CyclicBarrier用法实例大全

    在Java并发编程中,CountDownLatch和CyclicBarrier是两种非常重要的同步工具类,它们用于协调多个线程间的协作。这两个工具都是在`java.util.concurrent`包下,是Java并发库的重要组成部分。 **CountDownLatch** ...

    java并发编程中CountDownLatch和CyclicBarrier的使用借鉴.pdf

    java并发编程中CountDownLatch和CyclicBarrier是两个非常重要的线程控制和调度工具,经常被用于解决多线程程序设计中的线程等待问题。本文将对CountDownLatch和CyclicBarrier的使用场景和实现进行详细的介绍。 ...

    mybaits 多线程 实现数据批量插入 (运用CountDownLatch实现闭锁)

    总结来说,MyBatis结合多线程和CountDownLatch闭锁实现数据批量插入是一种高效且安全的方法。它不仅能显著提升数据处理速度,还能有效防止并发问题,是处理大数据量场景下的明智选择。在实际开发中,可以根据具体...

    java并发编程中CountDownLatch和CyclicBarrier的使用.pdf

    在Java并发编程中,CountDownLatch和CyclicBarrier是两种非常重要的同步工具,用于协调多个线程之间的交互。它们都属于java.util.concurrent包下的类,为多线程编程提供了强大的支持。 **CountDownLatch** 是一个...

    Java中的CountDownLatch与CyclicBarrier:深入理解与应用实践

    在Java的并发编程中,CountDownLatch和CyclicBarrier是两个非常重要的同步工具,它们用于协调多个线程的执行顺序。本文将详细介绍CountDownLatch和CyclicBarrier的工作原理、使用场景以及如何在实际项目中应用它们。...

    CountDownLatch 和 CyclicBarrier 的运用(含AQS详解)

    CountDownLatch 适用于“一个或多个线程等待其他线程完成任务”的场景,而 CyclicBarrier 更适合于“一组线程相互等待”的场景。通过了解它们的实现原理和应用场景,开发者可以更好地选择合适的工具来满足自己的需求...

    Java多线程并发访问解决方案

    在Java编程中,多线程并发...以上是Java多线程并发访问的主要解决方案,理解并熟练运用这些技术,可以有效地解决并发编程中遇到的问题,提升程序的稳定性和效率。在实际开发中,应根据具体需求选择合适的并发控制策略。

    并发编程实践,全面介绍基础知识、JVM同步原语、线程安全、低级并发工具、线程安全容器、高级线程协作工具、Executor部分等

    本文将全面介绍Java并发编程的基础知识、JVM同步原语、线程安全、低级并发工具、线程安全容器、高级线程协作工具以及Executor服务。 1. **基础知识** - **并发与并行**:并发是指多个任务在同一时间段内交替执行,...

    java并发编程专题(九)----(JUC)浅析CyclicBarrier

    与 CountDownLatch 不同,CyclicBarrier 的计数器可以被重置后使用,因此它被称为是循环的 barrier。CyclicBarrier 的构造函数有两个,分别是 `CyclicBarrier(int parties)` 和 `CyclicBarrier(int parties, ...

    高并发之-SimpleDateFormat类的线程安全问题和解决方案.docx

    SimpleDateFormat类的线程安全问题和解决方案 SimpleDateFormat类的线程安全问题 SimpleDateFormat类是Java提供的日期时间转化类,用于将日期和时间类型的数据进行解析和格式化。在Java开发中,SimpleDateFormat类...

    多线程 高并发

    通过分析和学习这些示例,我们可以更好地理解如何在实际项目中应用多线程和高并发技术。 总之,多线程和高并发是提升软件性能和扩展性的重要手段。在实践中,我们需要充分理解它们的原理,掌握相应的编程技巧,以及...

    详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    在Java并发编程中,CountDownLatch和CyclicBarrier都是用于协调多线程间同步的重要工具,它们可以帮助开发者在特定条件满足时启动或者结束线程的执行。本文将详细探讨这两个类的内部实现机制以及它们在实际应用场景...

    高并发多线程处理demo-java.rar

    在Java编程领域,高并发和多线程是关键的技术之一,尤其在服务器端应用和大数据处理中至关重要。这个"高并发多线程处理...通过学习和理解这些概念,开发者可以更好地掌握如何在Java中实现高效、安全的多线程并发处理。

    多线程countDownLatch方法介绍

    在Java多线程编程中,CountDownLatch是一个非常重要的同步工具类,它可以帮助我们协调多个线程之间的交互。本文将深入探讨CountDownLatch的工作...理解其工作原理和应用场景,能帮助开发者更好地设计和优化并发程序。

    笔记-2、线程的并发工具类2

    这些工具类极大地简化了并发编程中的同步和通信问题,使得开发者能够更安全、高效地编写多线程程序。以下是针对给定标题和描述中提到的一些主要并发工具类的详细解释: 1. **Fork-Join框架** Fork-Join框架是Java ...

    Java 模拟线程并发

    Java并发库还包含了很多其他有用的工具,如Semaphore(信号量)用于控制同时访问特定资源的线程数量,CyclicBarrier(循环屏障)和CountDownLatch(计数器门锁)用于多线程间的协作,以及Lock接口及其实现如...

    Java+并发性和多线程

    Java并发性和多线程是Java开发中至关重要的概念,它们涉及到如何在单个或多个处理器上同时执行程序的不同部分,从而提升程序的效率和响应速度。在这个领域,Java提供了丰富的工具和API,使得开发者能够有效地管理和...

    JUC代码收集,java高并发多线程学习

    - `CountDownLatch`:它允许一个或多个线程等待其他线程完成操作,常用于并发测试和初始化场景。 - `CyclicBarrier`和`Semaphore`:这两个是协调多线程的工具,前者让一组线程等待到达某个点后再一起继续,后者...

    利用 CountDownLatch 类实现线程同步

    在实际开发中,结合 `ExecutorService` 和 `Future`,我们可以构建更复杂的并发控制策略,以优化多线程程序的性能和可维护性。然而,也要注意,过度使用或不恰当使用同步工具可能导致死锁或性能下降,因此在设计并发...

Global site tag (gtag.js) - Google Analytics