`
Everyday都不同
  • 浏览: 725170 次
  • 性别: Icon_minigender_1
  • 来自: 宇宙
社区版块
存档分类
最新评论

多线程:如何确定所有任务都执行完成了?

 
阅读更多

--------------------20180502更新-----------------------------

今天学习到了一个比较强大的类:ExecutorCompletionService,它是将 Executor和BlockQueue结合的jdk类,其实现的主要目的是:提交任务线程,每一个线程任务直线完成后,将返回值放在阻塞队列中,然后可以通过阻塞队列的take()方法返回 对应线程的执行结果!!

所以还可以这样写:

 

ExecutorCompletionService<String> completionService = new ExecutorCompletionService(Executors.newFixedThreadPool(5));
        for(int i=0; i<10; i++) {
            int j = i;
            completionService.submit(()-> Thread.currentThread().getName() + "------>" + j);
        }

        try {
            for(int i=0; i<10; i++) {
                Future<String> future = completionService.take();
                if(future != null) {
                    String str = future.get();
                    System.out.println(str);
                }
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("---------->结束");

 同样可以达到阻塞的效果!(注:其中有用到jdk1.8的lambda表达式~)

---------------------------------------------------------------------------------------------------------

 

之前我有写过一篇博客,是关于多线程写同一个sheet文件的。类似的场景很多,当我们想用多线程提高效率时,面临的关键问题就是线程安全和确定所有任务都完成。线程安全的问题那篇博客有说,就是确保对公共资源的写操作是安全的,比如List的add操作采用synchronized来包装或直接采用线程安全的集合;Sheet的addRow加锁等… 而本篇的重点是“如何确保所有任务都完成,才能进行下一步?”。

 

先来看现象:

 

public static void m() {
    for(int i=0; i<10; i++) {
      int j = i;
      new Thread(new Runnable() {
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName() + "------>" + j);
        }
      }).start();
    }

    System.out.println("---------->结束");
  }

 这段代码创建了10个线程,每个线程的任务会打印当前线程的名字,运行后发现可能出现以下结果(顺序不一定是下面这样):

 

Thread-1------>1
---------->结束

Thread-0------>0
Thread-2------>2
Thread-4------>4
Thread-3------>3
Thread-5------>5
Thread-6------>6
Thread-7------>7
Thread-8------>8
Thread-9------>9

会发现“---------->结束”没有在所有线程都运行完就打印出来了,映射到实际场景就是用多线程去帮我们干活,还没干完呢就直接下一步了,如此的话没有实际意义(除非这个多线程的任务是异步的,其他逻辑不需要等待它完成才能进行)。

 

我们知道,多线程执行任务可以用原始的线程提交(上述代码),也可以用线程池(比较推荐这种方式,便于对线程进行管理)。为了解决上述问题,可以用CountDownLatch计数器,计数器的初始大小要跟任务数的大小一致(跟线程数无关),每执行一次任务,计数器减一(countDown),await()方法会一直阻塞主线程,直到计数器的值减为0,才会释放锁,如此便可以达到确保所有任务都完成才继续下一步的效果。

 

先用原始线程结合计数器的方式来试试效果:

 

public static void m1() {
    CountDownLatch latch = new CountDownLatch(10);
    for(int i=0; i<10; i++) {
      int j = i;
      new Thread(new Runnable() {
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName() + "------>" + j);
          latch.countDown();
        }
      }).start();
    }
    try {
      latch.await();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("---------->结束");
  }

 无论运行多少次,会发现"---------->结束"始终会在多线程所有任务都执行完毕后打印,比如某次结果:

 

Thread-0------>0
Thread-1------>1
Thread-2------>2
Thread-3------>3
Thread-4------>4
Thread-7------>7
Thread-8------>8
Thread-9------>9
Thread-5------>5
Thread-6------>6
---------->结束

 

再用线程池结合计数器的方式来尝试:

 

public static void m2() {
    CountDownLatch latch = new CountDownLatch(10);
    ExecutorService es = Executors.newFixedThreadPool(5);
    for(int i=0; i<10; i++) {
      int j = i;
      es.submit(new Runnable() {
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName() + "------>" + j);
          latch.countDown();
        }
      });
    }
    try {
      latch.await();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("---------->结束");
  }

 同样地,无论运行多少次,"---------->结束"都会在所有任务都完成以后再进行!比如某次打印结果为:

pool-1-thread-2------>1
pool-1-thread-2------>5
pool-1-thread-3------>2
pool-1-thread-1------>0
pool-1-thread-1------>8
pool-1-thread-1------>9
pool-1-thread-3------>7
pool-1-thread-4------>3
pool-1-thread-2------>6
pool-1-thread-5------>4
---------->结束

 

这充分印证了CountDownLatch计数器的强大!下面我们再看一个比较容易忽略的方式:

 

public static void m3() {
    ExecutorService es = Executors.newFixedThreadPool(5);

    List<Future<String>> list = new ArrayList<>();
    for(int i=0; i<10; i++) {
      int j = i;
      Future<String> future = es.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
          return Thread.currentThread().getName() + "------>" + j;
        }
      });
      list.add(future);
    }

    try {
      for(Future<String> future : list) {
        System.out.println(future.get());
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }

    System.out.println("---------->结束");
  }

 Callable不用多说,它可以表示一个有返回值的线程,Future则用于接收返回的结果。Future的get方法具有阻塞作用,它会一直阻塞直至获取到结果。Callable&Future一般都是结合线程池来使用。

运行多次,也会发现"---------->结束"总是在最后运行的,同样达到了目的。

 

说到线程池管理线程,需要注意的是比如:

 

Executors.newFixedThreadPool(40)

 

实际上是new了一个corePoolSize=maximumPoolSize的特殊情况的线程池:

 

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

 

Executors提供的静态方法创建线程池,内部都是去构造一个ThreadPoolExecutor,只是不同类型的线程池,

 

corePoolSize和maximumPoolSize的大小关系不同,还有采用的任务队列的类型也不同。

 

 

 

关于多线程和线程池的一些知识补充,见以下手工笔记:

 

1.一些多线程的基本概念:

 



 

 

2、线程池类型以及提交线程的过程:

 




 

3、常见的线程相关类的关系图:

 



 

 

 

 

 

  • 大小: 427.1 KB
  • 大小: 472.7 KB
  • 大小: 616.7 KB
分享到:
评论

相关推荐

    计算多线程环境下执行任务时间1

    在多线程编程中,准确地测量任务执行时间是一项挑战,因为并发执行使得任务的启动和结束时间难以确定。Java的`java.util.concurrent`包提供了一个名为`CountDownLatch`的工具类,它允许我们同步多个线程并计算它们的...

    12.1 Qt5多线程:多线程及简单实例

    Qt5框架提供了一种方便的方式来实现多线程,它允许开发者在不同的线程中执行任务,从而避免主线程(GUI线程)因处理耗时操作而变得卡顿。本知识点将深入探讨Qt5中的多线程以及一个简单的实例——WorkThread。 **1. ...

    多线程并行执行,汇总结果

    例如,在一个多线程并行执行的任务中,可能需要所有线程都执行完毕后才汇总结果。这时,可以设置一个`CountDownLatch`,初始化计数值为线程的数量,每个线程执行完后调用`countDown()`,最后主线程通过调用`await()`...

    Java多线程--等待所有子线程执行完的五种方法.docx

    在Java多线程编程中,有时我们需要确保所有子线程执行完毕后再进行后续操作,例如在并发测试、数据聚合或资源清理等场景。本篇文章将详细介绍五种在Java中等待所有子线程执行完的方法。 ### 方法一:使用`sleep`...

    java多线程之A线程等B、C线程都执行完在执行的结果示例

    Java多线程编程是开发高并发应用的关键技术之一,它涉及到如何有效地利用系统资源,提高程序的执行效率。在这个场景中,我们关注的是一个特定的多线程问题:A线程需要等待B线程和C线程执行完毕后再开始执行,同时...

    C#判断线程池中所有的线程是否已经完成

    当`workerThreads`等于`maxWorkerThreads`时,表明线程池中的所有工作者线程已经完成工作,因为此时没有更多的可用线程可以执行任务。这时,我们可以取消注册`rhw`,防止`CheckThreadPool`被再次调用,并在此处添加...

    C#线程池 所有线程运行完毕

    在C#编程中,线程池(ThreadPool)是一种高效的线程管理机制,它允许开发者...通过理解这些概念,开发者可以更好地掌握如何在C#中有效地使用线程池,实现多任务的并发执行,并确保在所有任务完成后正确地停止线程池。

    如何实现多线程多任务?

    在计算机编程中,多线程和多任务是提高应用程序效率和响应能力的重要技术。本文将深入探讨如何在C++环境中,特别是在MFC(Microsoft Foundation Classes)框架下实现多线程多任务。 首先,理解“多线程”和“多任务...

    C#多线程执行

    在编程领域,多线程是实现并发执行任务的关键技术,特别是在C#这样的现代编程语言中。C#提供了丰富的多线程支持,使得开发者...而“TaskSortRelationArrange”可能是对多线程任务调度和依赖管理的一个具体实践示例。

    多任务执行:多进程多线程介绍

    总结,多进程和多线程都是实现多任务执行的有效手段,各有优缺点。多进程适合资源密集型任务,多线程适合I/O密集型任务。在Python中,`multiprocessing`和`threading`模块提供了完善的工具支持。理解和熟练运用这些...

    Qt 之多线程处理多任务

    同时,`QRunnable`的`setAutoDelete(true)`属性可以确保任务执行完毕后,其对象会被自动删除,释放资源。 解决假死问题的关键在于避免在主线程(GUI线程)中执行耗时操作。Qt的事件驱动模型使得主线程可以专注于...

    多线程多任务下载软件.zip易语言项目例子源码下载

    1. **多线程技术**:在计算机科学中,多线程是指在一个程序中同时运行多个独立的执行路径,每个路径称为一个线程。在下载软件中,多线程能够提高下载速度,因为它可以同时从服务器获取数据,而不是顺序地逐块下载。...

    多个线程到达后才能执行某个任务,并且只能执行一次

    标题中的“多个线程到达后才能执行某个任务,并且只能执行一次”指的是在多线程环境下,需要确保一个任务在所有等待线程都到达特定条件后才开始执行,并且这个任务在整个程序运行过程中只被执行一次。这通常涉及到...

    多线程计数实现多线程执行后再执行主线程

    ### 多线程计数实现多线程执行后再执行主线程 #### 一、知识点概述 本文将深入探讨如何利用`CountDownLatch`机制来确保多个子线程完成各自的任务后,再让主线程继续执行的方法。这种方法在项目开发中非常实用,...

    一个高频问题:异步操作会创建线程吗?.doc

    在这种情况下,系统只能从线程池中申请更多的线程来从q1队列中提取任务。当某个时刻,已暂停线程队列中的线程激活,那么它又回到了已释放队列中继续执行任务。 因此,异步操作会创建线程吗?答案是可能会,也有可能...

    易语言源代码_多线程多任务下载软件.zip

    在这个“易语言源代码_多线程多任务下载软件.zip”压缩包中,包含了一个使用易语言编写的多线程多任务下载软件的源代码,主要文件名为“多线程多任务下载软件.e”。 多线程技术在软件开发中扮演着重要角色,尤其是...

    多线程执行完后主程序再执行(包括需要子线程返回结果)

    在编程领域,多线程是实现并发执行任务的重要方式,特别是在服务器端开发和高性能计算中。标题提到的“多线程执行完后主程序再执行(包括需要子线程返回结果)”是一个典型的多线程同步问题。在这个场景中,主程序会...

    多线程实现任务管理器

    2. **同步执行**:在多线程环境中,同步执行意味着所有任务需要按照一定的顺序依次完成,而不是并发执行。这通常可以通过`Task.WaitAll()`方法来实现,传入所有要等待的任务实例,直到它们全部完成。 ```csharp Task...

    Java多线程机制(讲述java里面与多线程有关的函数)

    Java多线程机制是Java编程中至关重要的一部分,它允许程序同时执行多个任务,提升应用程序的效率和响应性。以下是对各个知识点的详细说明: 9.1 Java中的线程: Java程序中的线程是在操作系统级别的线程基础上进行...

    大数据量多线程执行分页查询

    2. **多线程**:多线程是并发编程的一种方式,允许多个任务同时执行,提高系统资源利用率。在大数据量查询中,通过创建多个线程并行处理数据,可以显著减少整体处理时间。 3. **分页查询**:分页是一种优化大数据量...

Global site tag (gtag.js) - Google Analytics