多线程 ExecutorService CountDownLatch使用

 

    ThreadUtil.execAsync(() -> {
                System.out.printf("xxx);
            });
导出表格1W+数据,十个线程同时跑,全部跑完再导出excel  
@ApiOperation(value="预览excel", nickname="预览excel")
    @PostMapping(value = "importExcelData", produces = { "application/json; charset=utf-8" })
    public String importExcelData(HttpServletRequest request,
                                  @ApiParam(name="file", value="file", required=false) MultipartFile file) {
        ExecutorService pool = new ThreadPoolExecutor(10, 10,0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
        List<VideoEntity> all =new ArrayList<>();
        try {
            byte [] byteArr = file.getBytes();
            InputStream bookStream = new ByteArrayInputStream(byteArr);
            ExcelReader reader = ExcelUtil.getReader(bookStream);
            List<Map<String, Object>> list = reader.readAll();
            CountDownLatch endSigle = new CountDownLatch(list.size());

            list.stream().forEach(e->{
                pool.execute(new Runnable() {
                    @Override
                    public void run() {
                        String  videoName = e.get("视频名称")+"";
                        String  coursePackageName = e.get("书本名称")+"";
                        String name = videoName.substring(0,videoName.length()-4);
                        QueryWrapper q = new QueryWrapper();
                        q.like("name",name);
                        List<VideoEntity>  videoEntityList = videoService.selectList(q);
                        if(CollectionUtil.isNotEmpty(videoEntityList)){
                            List<Integer> ids = Optional.ofNullable(videoEntityList).orElse(new ArrayList<>()).stream().map(VideoEntity::getId).collect(Collectors.toList());
                            e.put("精品视频是否有记录","是") ;
                            e.put("精品视频id", JSONObject.toJSONString(ids)) ;
                        }

                        QueryWrapper qq = new QueryWrapper();
                        qq.like("name",name);
                        List<VideoCmsAdminEntity>  videoCmsAdminList = videoCmsAdminService.selectList(qq);
                        if(CollectionUtil.isNotEmpty(videoCmsAdminList)){
                            List<Integer> ids = Optional.ofNullable(videoCmsAdminList).orElse(new ArrayList<>()).stream().map(VideoCmsAdminEntity::getId).collect(Collectors.toList());
                            QueryWrapper query = new QueryWrapper();
                            query.in("video_id",ids);
                            List<VideoTranscodingManagementEntity> listData = videoTranscodingManagementService.selectList(query);
                            if(!CollectionUtil.isEmpty(listData)){
                                e.put("转码是否有记录","是") ;
                            }else {
                                e.put("转码是否有记录","否") ;
                            }
                        }
                        endSigle.countDown();
                        System.out.printf("线程池已经执行的和未执行的任务总数:"+endSigle.getCount()+"====");
                    }
                });
            });
            try {
                endSigle.await();
                pool.shutdown();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            ExcelWriter writer = ExcelUtil.getWriter(new File("C:\\Users\\K0000173\\Desktop\\jpsp\\转码相关\\"+file.getOriginalFilename()+"_data.xlsx"));
            writer.write(list);
            writer.close();

        } catch (RuntimeException e) {
            logger.error(e.getMessage());
            return  buildFailedResult("获取数据失败",all);
        } catch (IOException e) {
            logger.error(e.getMessage());
            return  buildFailedResult("获取数据失败",all);
        }
        return buildSuccessResult("获取数据成功",all);
    }

 

  public Integer aaa() {
        List<Integer> list =new ArrayList<>();
        ExecutorService pool = new ThreadPoolExecutor(40, 40,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
        CountDownLatch endSigle = new CountDownLatch(list.size());
        List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
        for (Integer data:list) {
            Future<Integer> future = pool.submit(new Callable<Integer>() {
                @Override
                public Integer  call() throws Exception {
                    try {
                        System.out.printf(data+"");
                    } catch (Exception e) {
                    }
                    endSigle.countDown();
                    return data;
                }
            });
          futures.add(future);  
        }
        try {
            endSigle.await();
            pool.shutdown();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        for (Future<Integer> future:futures ) {
            try {
                Integer data =  future.get();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
       
        return 1;
    }

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

二、 ExecutorService 的submit() 与execute()区别 
1、 submit()可以接收runnable无返回值和callable有返回值 
      execute()接收runnable 无返回值

2、submit有返回值,而execute没有  


三、关闭线程池方法 shotdown() showdownNow()区别 

当我们使用shutdownNow方法关闭线程池时,一定要对任务里进行异常捕获。

当我们使用shuwdown方法关闭线程池时,一定要确保任务里不会有永久阻塞等待的逻辑,否则线程池就关闭不了。

最后,一定要记得,shutdownNow和shuwdown调用完,线程池并不是立马就关闭了,要想等待线程池关闭,还需调用awaitTermination方法来阻塞等待。


shutdown() 关闭了提交通道,用submit()是无效(停止接收新任务)原来的任务继续执行
shutdownNow() 停止接收新任务,原来的任务停止执行


CountDownLatch是JDK提供给我们的多线程间通信的一个工具,用于让主线程知道任务完成的进度。
如果需要等待所有的线程在执行完后触发一个操作,这个时候你就需要用到CountDownLatch。

//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException { };   
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
//将count值减1
public void countDown() { };  

public void doSome(int num){
     // 创建一个线程池,线程数2
     ExecutorService pool = Executors.newFixedThreadPool(2);
     // CountDownLatch用来监控子线程
     CountDownLatch endSigle = new CountDownLatch(2);
     Callable<Xxaaa> xxxCall = new Xxaaa(a);
     Callable<Xxaaa1> xxxCall1 = new Xxaaa1(a);
     Future<Xxaaa> ocrSearchFuture = pool.submit(xxxCall);//1
     Future<Xxaaa> ocrSearchFuture = pool.submit(xxxCall1);//2
     Xxaaa xxaaa =ocrSearchFuture.get()
     try{
         // 等待两个线程执行结束     
         endSigle.await();
         new Thread(new UserCall(paramMap,.......).start();
     }
     catch (Exception e){
         LOGGER.info(e);
         return null;
     }
     finally{
         if (null != pool && !pool.isShutdown()){
             pool.shutdown();
         }
     }
 }
 public class Xxaaa implements Callable<Xxaaa>{
     private String a;
     public OcrSearchCall(a){
         this.a = a;
     }
     @SuppressWarnings("finally")
     public xxaaa call(){
         //将count值减1,
         endSigle.countDown();

         Xxaaa xxaaa = null;
         return xxaaa;
     }

     public class UserCall implements Runnable{
         //基本参数
         private Map<String, Object> paramMap;
         public UserCall(Map<String, Object> paramMap,......){
             this.paramMap = paramMap;
......
         }

         @SuppressWarnings("finally")
         public void run(){
             int a=1;
         }
     }

实例:

/* 线程池可以把线程复用起来,减少线程创建销毁的时间和资源消耗,提高了程序任务执行的吞吐率。
    就像线程属于全局使用的资源一样,线程池一般也是全局性,对整个应用进程的线程复用做有效的管理。设计者一般都会把线程池作为类的静态成员或者单例成员,存活于整个进程的生命周期。*/

   /* 如果没有设置核心线程数,比如 newCachedThreadPool ,在线程池的线程空闲时间到达 60s 后,线程会关闭,所有线程关闭后线程池也相应关闭回收。
    如果设置了核心线程数,比如 newSingleThreadExecutor 和 newFixedThreadPool ,如果没有主动去关闭,或者设置核心线程的超时时间,核心线程会一直存在不会被关闭,这个线程池就不会被释放回收。
   */
   //newFixedThreadPool线程数设置50,不会被回收
     private   ExecutorService pool = Executors.newFixedThreadPool(2);
 @RequestMapping("/testUserInfoCall")
    @ResponseBody
    public String testUserInfoCall(){
        String result="";
        try{
            long startTime=System.currentTimeMillis();
            for (int i = 0; i < 3; i++) {

                result+= getAreaPress();

            }
            long endTime=System.currentTimeMillis()-startTime;
            System.out.println("整个方法耗时:"+endTime+"ms");
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        return result;
    }

    public String getAreaPress(){
        String result="打印-----=";
        try{
                long startTime=System.currentTimeMillis();
                /* 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
               第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
               第三:提高线程的可管理性。*/
                CountDownLatch flag = new CountDownLatch(2);
                Callable<Integer> userInfoCall = new UserInfoCall();
                Future<Integer> userInfoFuture = pool.submit(userInfoCall);
                int activeCount = ((ThreadPoolExecutor)pool).getActiveCount();
                long taskCount= ((ThreadPoolExecutor) pool).getTaskCount();
                long completedTaskCount= ((ThreadPoolExecutor) pool).getCompletedTaskCount();
                int  getPoolSize=((ThreadPoolExecutor) pool).getPoolSize();
                int getLargestPoolSize=((ThreadPoolExecutor) pool).getLargestPoolSize();

                System.out.println("活跃线程数:"+activeCount);
                System.out.println("线程池已经执行的和未执行的任务总数:"+taskCount);
                System.out.println("线程池当前的线程数量:"+getPoolSize);
                System.out.println("线程池已完成的任务数量,该值小于等于taskCount:"+completedTaskCount);
                System.out.println("线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了maximumPoolSize:"+getLargestPoolSize);
                //向线程池中提交任务的submit方法不是阻塞方法,而Future.get方法是一个阻塞方法,
                // 当submit提交多个任务时,只有所有任务都完成后,才能使用get按照任务的提交顺序得到返回结果,
                // 所以一般需要使用future.isDone先判断任务是否全部执行完成,完成后再使用future.get得到结果
                // 。(也可以用get (long timeout, TimeUnit unit)方法可以设置超时时间,防止无限时间的等待)
                // Integer a = userInfoFuture.get(6000, TimeUnit.MILLISECONDS);
                //userInfoFuture.get()等待线程返回结果才能继续向下执行-》如果不使用则直接不等线程执行继续往下
                //userInfoFuture.get();
                long endTime=System.currentTimeMillis()-startTime;
                System.out.println("耗时:"+endTime+"ms");

        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        return result;
    }

注意此处:Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。

Future<Integer> userInfoFuture = pool.submit(userInfoCall);
userInfoFuture.get()

final CountDownLatch endSign = new CountDownLatch(3);

endSign .await() 当为0时候才可以继续执行下去

  public static void main(String[] args) {
        try{
            List<Future<Integer>> list=null;
            long startTime=System.currentTimeMillis();
            list= getAreaPress111();
            for (int i = 0; i < list.size(); i++) {
                Integer flag= list.get(i).get();
                System.out.println("====="+i+"========"+flag);
            }
            long endTime=System.currentTimeMillis()-startTime;
            System.out.println("整个方法耗时:"+endTime+"ms");
        }catch (Exception e){
            System.out.println("异常:"+e);
        }
    }

    private static   List<Future<Integer>> getAreaPress111(){
        List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
        ExecutorService pool = Executors.newFixedThreadPool(3);
        final CountDownLatch endSign = new CountDownLatch(3);
        try {
            for (int i = 0; i < 3; i++) {
                Callable<Integer> userInfoCall = new UserInfoCall(endSign);
                Future<Integer> future = pool.submit(userInfoCall);
             /*   Future<Integer> future = pool.submit(new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
                        Thread.sleep(5000);
                        endSign.countDown();
                        System.out.println("sss"+endSign.getCount());
                        return 1;
                    }
                });*/
                futures.add(future);
            }
            //等待三个线程执行结束 才能继续向下执行
            endSign.await();
            pool.shutdown();
        } catch (InterruptedException e) {
            System.out.println(e);
        }
        return futures;
    }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以使用CountDownLatch配合Future和Callable实现返回子线程的结果。以下是示例代码: ```java import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ThreadDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newCachedThreadPool(); CountDownLatch latch = new CountDownLatch(1); Future<Integer> future = executorService.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { int result = 0; // 执行一些耗时的操作 for (int i = 0; i < 1000000; i++) { result += i; } return result; } }); new Thread(() -> { try { latch.await(); System.out.println("子线程返回结果:" + future.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }).start(); // 唤醒等待的子线程 latch.countDown(); executorService.shutdown(); } } ``` 在上面的代码中,我们创建了一个CountDownLatch对象,然后在主线程中启动了一个子线程,子线程执行了一些耗时的操作,返回了结果。在主线程中,我们使用Future对象获取了子线程的返回结果,并且在启动了一个新的线程来处理这个返回结果。在新线程中,我们使用CountDownLatch的await()方法等待主线程调用countDown()方法唤醒它,然后再通过Future的get()方法获取子线程的返回结果。最后,我们调用ExecutorService的shutdown()方法关闭线程池。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值