java并发包中CountDownLatch和线程池的使用详解

1.CountDownLatch

现在做的这个华为云TaurusDB比赛中,参考的之前参加过阿里的PolarDB大赛的两个大佬的代码,发现都有用到CountDownLatch这个类,之前看代码的时候也看过,但是没有搞得很明白,自己写也写不出来,在此自己先学习一下。

字面理解:CountDownLatch:数量减少的门栓。

创建这样一个门栓

CountDownLatch countDownLatch = new CountDownLatch(count);

参数:count,门栓的计数次数。

在所有线程执行完成之前,调用countDownLatch.await()阻塞主线程。

每当一个线程执行完一个指定动作之后,count就会减少1,当count等于0时,主线程不再阻塞,开始继续执行下面的代码,当count大于0时,主线程一直阻塞,等待count变为0。每个线程动作执行结束后,执行countDownLatch.countDown(),这个门栓的count减一。

int ThreadNum = 16;
CountDownLatch countDownLatch = new CountDownLatch(ThreadNum);
for(int i = 0; i < ThreadNum ; i++){
 final int finalI = i;
 new Thread(() -> {
  int n = 0;
  System.out.println("线程应该做的事情");
  while(n < 10){
   n++;
  }
  countDownLatch.countDown();
 }).start();
}
try{
 countDownLatch.await();
}catch(InterruptedException e){
 logger.infor("InterruptedException!!");
}

2.线程池

其实线程池之前的ipv6的项目里用过,但是也忘记得差不多了,复习一下。

线程在创建和关闭时都需要花费时间,如果为每一个小的任务都创建一个线程,可能创建和销毁线程所用的时间会多于该线程真实工作所消耗的时间,就会得不偿失。除了时间,空间也需要考虑,线程本身也是要占用内存空间的,大量的线程会食用过多的内存资源,可能会造成OOM。另外在回收时,大量的线程会延长GC的停顿时间。

因此在生产环境中使用线程必须对其加以控制和管理

使用线程池之后,创建线程变成了从线程池中获得空闲的线程,关闭线程变成了归还线程给线程池。

通过ThreadPoolExecutor可以创建一个线程池,ThreadPoolExecutor实现了Executors接口。

举个栗子:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolTest {
 public static void main(String[] args) {
  ThreadPoolExecutor pool = new ThreadPoolExecutor(10,20,60,
    TimeUnit.SECOUNDS,new ArrayBlockingQueue<Runnable>(15000),new ThreadFactory(){
   private AtomicInteger threadId = new AtomicInteger(0);
   @Override
   public Thread newThread(Runnable r){
    Thread thread = new Thread(r);
    thread.setDaemon(true);
    String prefix = "thread-";
    thread.setName(prefix+threadId.incrementAndGet());
    return thread;
   }
  });
 }
}

这样就创建了一个线程池。参数依次解释:

corePoolSize:指定了线程池中线程的数量,线程池中可以有10个存活的线程

maximumPoolSize:指定了线程池中最大的线程数,线程池中最多能有20个存活的线程

keepAliveTime:当线程池中的数量超过corePoolSize时,这些线程在多长时间会被销毁,60s

unit:keepAliveTime的单位

workQueue:任务队列,被提交但是没有被执行的任务存在的地方。他是一个BlockingQueue<Runnable>接口的对象。

threadFactory:线程工厂,你想创建什么样子的线程

重点说一下workQueue:

根据队列的功能分类,可以使用以下几种BlockingQueue接口

补充:Java中CountDownLatch,CyclicBarrier以及Semaphore的使用场景

Java并发包中提供了很多有用的工具类来帮助开发者进行并发编程,今天我就来说说CountDownLatch,CyclicBarrier以及Semaphore这三个的用法和使用场景。

1.CountDownLatch使用场景和用法

CountDownLatch一般是用于某个线程等待其他线程执行完之后,它才能执行。例如一家人在等待爸爸妈妈回家,才能进行晚宴,示例代码如下:

public class CountDownLatchTest {

 public static void main(String[] args) throws Exception {
  final CountDownLatch cdl = new CountDownLatch(2);
  new Thread(){
   public void run() {
    try {
     System.out.println("等待老爸回家...");
     Thread.sleep(5000);
     cdl.countDown();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }

   };
  }.start();

  new Thread(){
   public void run() {
    try {
     System.out.println("等待老妈回家...");
     Thread.sleep(5000);
     cdl.countDown();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   };
  }.start();

  cdl.await();
  System.out.println("老爸老妈回来了...");
  System.out.println("晚宴开始了...");
 }

}

2.CyclicBarrier(栅栏)使用场景和用法

CyclicBarrier一般是一组线程等待至某个状态,然后这一组线程才能同时执行(感觉跟CountDownLatch有点类似啊,不过仔细想想还是有差别的,感觉容易混淆)。

代码示例如下:

public class CyclicBarrierTest {

 public static void main(String[] args) {
  int count = 3;
  CyclicBarrier cb = new CyclicBarrier(count, new Runnable() {
   @Override
   public void run() {
    //此处所有线程都调用了await方法之后,会走到这里
    System.out.println("所有线程操作完成之后都调用了await方法");
   }
  });

  for(int i=0;i<count;i++){
   new WriteLogHandler(cb).start();
  }
 }

 static class WriteLogHandler extends Thread{

  private CyclicBarrier cb = null;

  public WriteLogHandler(CyclicBarrier cb) {
   this.cb = cb;
  }

  @Override
  public void run() {
   try {
    System.out.println("线程:" + Thread.currentThread().getName() + "开始写日志");
    Thread.sleep(2000);
    System.out.println("线程:" + Thread.currentThread().getName() + "写日志结束,等待其他线程");
    cb.await();

    System.out.println("所有线程写日志数据结束,继续其他操作");
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
 }

}

3.Semaphore(信号量)使用场景和用法

Semaphore类似锁的用法,用于控制对某资源的访问权限,示例代码如下:
public class SemaphoreTest {

 public static void main(String[] args) {
  ExecutorService executor = Executors.newCachedThreadPool();
  final Semaphore semaphore = new Semaphore(5);

  for(int i=0;i<10;i++){
   final int num = i;
   executor.execute(new Runnable() {
    @Override
    public void run() {
     try {
      semaphore.acquire();
      System.out.println("正在执行任务" + num);
      Thread.sleep((long)Math.random() * 1000);
      System.out.println("任务" + num + "执行结束");
      semaphore.release();
     } catch (Exception e) {
      e.printStackTrace();
     }
    }
   });
  }
  executor.shutdown();
 }

}

以上就是这三个并发工具类的使用场景和示例,仅为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。欢迎大家一起交流。

(0)

相关推荐

  • java并发学习-CountDownLatch实现原理全面讲解

    CountDownLatch在多线程并发编程中充当一个计时器的功能,并且维护一个count的变量,并且其操作都是原子操作. 如下图,内部有下static final的Sync类继承自AQS. 该类主要通过countDown()和await()两个方法实现功能的,首先通过建立CountDownLatch对象,并且传入参数即为count初始值. 如果一个线程调用了await()方法,那么这个线程便进入阻塞状态,并进入阻塞队列. 如果一个线程调用了countDown()方法,则会使count-1:当c

  • ThreadPoolExecutor线程池原理及其execute方法(详解)

    jdk1.7.0_79 对于线程池大部分人可能会用,也知道为什么用.无非就是任务需要异步执行,再者就是线程需要统一管理起来.对于从线程池中获取线程,大部分人可能只知道,我现在需要一个线程来执行一个任务,那我就把任务丢到线程池里,线程池里有空闲的线程就执行,没有空闲的线程就等待.实际上对于线程池的执行原理远远不止这么简单. 在Java并发包中提供了线程池类--ThreadPoolExecutor,实际上更多的我们可能用到的是Executors工厂类为我们提供的线程池:newFixedThreadP

  • java多线程CountDownLatch与线程池ThreadPoolExecutor/ExecutorService案例

    1.CountDownLatch: 一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行. 2.ThreadPoolExecutor/ExecutorService: 线程池,使用线程池可以复用线程,降低频繁创建线程造成的性能消耗,同时对线程的创建.启动.停止.销毁等操作更简便. 3.使用场景举例: 年末公司组织团建,要求每一位员工周六上午8点到公司门口集合,统一乘坐公司所租大巴前往目的地. 在这个案例中,公司作为主线程,员工作为子线程. 4.代码示例: package

  • 如何使用CountDownLatch同步java多线程

    最近写了一个并发幂等测试,用线程池加入多个线程,同时启动,领导觉得这样有一定的风险,要求更严格一点,把所有的线程加入池中,然后同时启动. 本来有多种方法,因为我们需要从多个线程中获取返回值,所以我们用CountDownLatch来同步多线程.CyclicBarrier也是可以同步多线程的,但因为其无法获取返回值,最后只能选择CountDownLatch. 因公司的代码不便共享,这里只提供一小部分代码. CountDownLatch latch = new CountDownLatch(1); <

  • java并发包中CountDownLatch和线程池的使用详解

    1.CountDownLatch 现在做的这个华为云TaurusDB比赛中,参考的之前参加过阿里的PolarDB大赛的两个大佬的代码,发现都有用到CountDownLatch这个类,之前看代码的时候也看过,但是没有搞得很明白,自己写也写不出来,在此自己先学习一下. 字面理解:CountDownLatch:数量减少的门栓. 创建这样一个门栓 CountDownLatch countDownLatch = new CountDownLatch(count); 参数:count,门栓的计数次数. 在所

  • Java中线程池自定义实现详解

    目录 前言 线程为什么不能多次调用start方法 线程池到底是如何复用的 前言 最初使用线程池的时候,网上的文章告诉我说线程池可以线程复用,提高线程的创建效率.从此我的脑海中便为线程池打上了一个标签——线程池可以做到线程的复用.但是我总以为线程的复用是指在创建出来的线程可以多次的更换run()方法的内容,来达到线程复用的目的,于是我尝试了一下.同一个线程调用多次,然后使run的内容不一样,但是我发现我错了,一个线程第一次运行是没问题的,当再次调用start方法是会抛出异常(java.lang.I

  • java 打造阻塞式线程池的实例详解

    java 打造阻塞式线程池的实例详解 原来以为tiger已经自带了这种线程池,就是在任务数量超出时能够阻塞住投放任务的线程,主要想用在JMS消息监听. 开始做法: 在ThreadPoolExcecutor中代入new ArrayBlockingQueue(MAX_TASK). 在任务超出时报错:RejectedExecutionException. 后来不用execute方法加入任务,直接getQueue().add(task), 利用其阻塞特性.但是发现阻塞好用了,但是任务没有被处理.一看Qu

  • Java线程池Executor用法详解

    目录 线程池类图 线程池的好处 new Thread的弊端 线程池核心类-ThreadPoolExecutor 使用Executors创建线程池 Executors.newCachedThreadPool Executors.newSingleThreadExecutor Executors.newFixedThreadPool Executors.newScheduledThreadPool 总结 如何定义线程池参数 线程池类图 我们最常使用的Executors实现创建线程池使用线程主要是用上

  • Java四种线程池的使用详解

    Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,

  • 详解Java多线程编程中CountDownLatch阻塞线程的方法

    直译过来就是倒计数(CountDown)门闩(Latch).倒计数不用说,门闩的意思顾名思义就是阻止前进.在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程. CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. CountDownLatch 的作用和 Thread.join() 方法类似,可用于一组线程和另外一组线程的协作.例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工

  • 深入java线程池的使用详解

    在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,任务的提于交和执行是同时进行的,如果你想对任务的执行进行调度或是控制 同时执行的线程数量就需要额外编写代码来完成.5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接 池的线程池来执行任务.这个架构主要有三个接口和其相应的具体类组成.这三个接口是Executor, ExecutorService.ScheduledExecutorService,让我们先用一个图

  • JAVA线程池原理实例详解

    本文实例讲述了JAVA线程池原理.分享给大家供大家参考,具体如下: 线程池的优点 1.线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用. 2.可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃. 线程池的创建 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQu

  • 模拟简单Java线程池的方法详解

    目录 一. 前言 二.线程池是什么? 三.线程池构造方法ThreadPoolExecutor的构造方法的参数都是啥意思? 四.模拟实现一个线程池 总结 一. 前言 为了实现并发编程,于是就引入了进程这个概念.进程就相当于操作系统的一个任务.多个进程同时执行任务,就实现了并发编程,能够更快的执行. 但是由于进程还不够轻量,创建一个进程,销毁一个进程消耗的资源不可忽视.如果进程数量不多的情况下,这些资源消耗是可以接受的,但是如果频繁的创建.销毁进程.就是一笔很大的开销了. 那要怎么办呢? 为了解决这

  • java开发MyBatis中常用plus实体类注解符详解

    目录 mybatis-plus常用注解符 1. 表名注解(@TableName) 2. 主键注解(@TableId) 3. 属性注解(@TableField) mybatis-plus常用注解符 1. 表名注解(@TableName) 作用:实体类和数据库中表建立对应关系:如 @TableName("thotset") public class HotsetEntity implements Serializable { private static final long serial

随机推荐