• 判断线程池中的线程是否全部执行完毕


    转:

    判断线程池中的线程是否全部执行完毕

    在使用多线程的时候有时候我们会使用 java.util.concurrent.Executors的线程池,当多个线程异步执行的时候,我们往往不好判断是否线程池中所有的子线程都已经执行完毕,但有时候这种判断却很有用,例如我有个方法的功能是往一个文件异步地写入内容,我需要在所有的子线程写入完毕后在文件末尾写“---END---”及关闭文件流等,这个时候我就需要某个标志位可以告诉我是否线程池中所有的子线程都已经执行完毕,我使用这种方式来判断。

    复制代码
    public class MySemaphore {
    
        public static void main(String[] args) throws IOException, InterruptedException {
            final File stream = new File("c:\temp\stonefeng\stream.txt");
            final OutputStream os = new FileOutputStream(stream);
            final OutputStreamWriter writer = new OutputStreamWriter(os);
            final Semaphore semaphore = new Semaphore(10);
            ExecutorService exec = Executors.newCachedThreadPool();
            
            final long start = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
                final int num = i;
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            semaphore.acquire();
                            writer.write(String.valueOf(num)+"
    ");
                            semaphore.release();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };
                exec.submit(task);
            }
            exec.shutdown();
            while(true){  
               if(exec.isTerminated()){  
                    writer.write("---END---
    ");
                    writer.close();
                    System.out.println("所有的子线程都结束了!");  
                    break;  
                }  
                Thread.sleep(1000);    
            }
            final long end = System.currentTimeMillis();
            System.out.println((end-start)/1000);
        }
    }
    复制代码

    当调用ExecutorService.shutdown方法的时候,线程池不再接收任何新任务,但此时线程池并不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。在调用shutdown方法后我们可以在一个死循环里面用isTerminated方法判断是否线程池中的所有线程已经执行完毕,如果子线程都结束了,我们就可以做关闭流等后续操作了。

    判断线程池中的线程是否全部执行完毕的另外一种解决方案则是使用闭锁(CountDownLatch)来实现,CountDownLatch是一种灵活的闭锁实现,它可以使一个或多个线程等待一组事件发生。闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示需要等待的事件数量。countDown方法递减计数器,表示有一个事件已经发生了,而await方法等待计数器达到零,即表示需要等待的事情都已经发生。可以使用闭锁来这样设计程序达到目的:

    复制代码
     1 public class CountDownLatchApproach {
     2     public static void main(String[] args) throws IOException, InterruptedException {
     3         final int nThreads = 10;
     4         final CountDownLatch endGate = new CountDownLatch(nThreads);
     5         final File stream = new File("c:\temp\stonefeng\stream.txt");
     6         final OutputStream os = new FileOutputStream(stream);
     7         final OutputStreamWriter writer = new OutputStreamWriter(os);
     8         ExecutorService exec = Executors.newCachedThreadPool();
     9         for (int i = 0; i < nThreads; i++) {
    10             final int num = i;
    11             Runnable task = new Runnable() {
    12                 @Override
    13                 public void run() {
    14                     try {
    15                         writer.write(String.valueOf(num)+"
    ");
    16                     } catch (IOException e) {
    17                         e.printStackTrace();
    18                     } finally {
    19                         endGate.countDown();
    20                     }
    21                 }
    22             };
    23             exec.submit(task);
    24         }
    25         endGate.await();
    26         writer.write("---END---
    ");
    27         writer.close();
    28     }
    29 }
    复制代码

    这种解决方案虽然可以达到目的但是性能差到没朋友,我更倾向于使用第一种方案。

    现在我们有了更优雅的第三种方案,它的执行性能也不错。

    复制代码
     1 public class MySemaphore {
     2 
     3     public static void main(String[] args) throws IOException, InterruptedException {
     4         final File stream = new File("c:\temp\stonefeng\stream.txt");
     5         final OutputStream os = new FileOutputStream(stream);
     6         final OutputStreamWriter writer = new OutputStreamWriter(os);
     7         final Semaphore semaphore = new Semaphore(10);
     8         ExecutorService exec = Executors.newCachedThreadPool();
     9         
    10         final long start = System.currentTimeMillis();
    11         for (int i = 0; i < 10000000; i++) {
    12             final int num = i;
    13             Runnable task = new Runnable() {
    14                 @Override
    15                 public void run() {
    16                     try {
    17                         semaphore.acquire();
    18                         writer.write(String.valueOf(num)+"
    ");
    19                         semaphore.release();
    20                     } catch (IOException e) {
    21                         e.printStackTrace();
    22                     } catch (InterruptedException e) {
    23                         e.printStackTrace();
    24                     }
    25                 }
    26             };
    27             exec.submit(task);
    28         }
    29         exec.shutdown();
    30         exec.awaitTermination(1, TimeUnit.HOURS);
    31         writer.write("---END---
    ");
    32         writer.close();
    33         System.out.println("ËùÓеÄ×ÓÏ̶߳¼½áÊøÁË£¡");  
    34         final long end = System.currentTimeMillis();
    35         System.out.println((end-start)/1000);
    36     }
    37 }
    复制代码
  • 相关阅读:
    利用子查询解决复杂sql问题
    如何用临时表代替游标进行表记录的拷贝
    SQL新函数, 排名函数 ROW_NUMBER(), RANK(), DENSE_RANK()
    SQL SERVER2005 中的错误捕捉与处理
    用户自定义函数代替游标进行循环拼接
    使用游标进行循环数据插入
    Oracle中利用存储过程建表
    SQL SERVER中强制类型转换cast和convert的区别
    SQL如何修改被计算字段引用的字段类型
    1.官方网站:http://www.mplayerhq.hu/design7/dload.html
  • 原文地址:https://www.cnblogs.com/libin6505/p/10576797.html
Copyright © 2020-2023  润新知