在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动、调度、管理线程的一大堆API了。在Java5以后,通过Executor来启动线程比用 Thread的start()更好。在新特征中,可以很容易控制线程的启动、执行和关闭过程,还可以很容易使用线程池的特性。

  一、创建任务

  任务就是一个实现了Runnable接口的类。

  创建的时候实run方法即可。

  二、执行任务

  通过java.util.concurrent.ExecutorService接口对象来执行任务,该接口对象通过工具类java.util.concurrent.Executors的静态方法来创建。

  Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。

   ExecutorService提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以关闭 ExecutorService,这将导致其停止接受新任务。关闭后,执行程序将最后终止,这时没有任务在执行,也没有任务在等待执行,并且无法提交新任务。

  executorService.execute(new TestRunnable());

  1、创建ExecutorService

  通过工具类java.util.concurrent.Executors的静态方法来创建。

  Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。

  比如,创建一个ExecutorService的实例,ExecutorService实际上是一个线程池的管理工具:

  ExecutorService executorService = Executors.newCachedThreadPool();

  ExecutorService executorService = Executors.newFixedThreadPool(3);

  ExecutorService executorService = Executors.newSingleThreadExecutor();

  2、将任务添加到线程去执行

  当将一个任务添加到线程池中的时候,线程池会为每个任务创建一个线程,该线程会在之后的某个时刻自动执行。

  三、关闭执行服务对象

  executorService.shutdown();

  四、综合实例

package concurrent;

import java.util.concurrent.ExecutorService; 

import java.util.concurrent.Executors;

public class TestCachedThreadPool { 

        public static void main(String[] args) { 

//                ExecutorService executorService = Executors.newCachedThreadPool(); 

                ExecutorService executorService = Executors.newFixedThreadPool(5); //         ExecutorService executorService = Executors.newSingleThreadExecutor();

for (int i = 0; i < 5; i++) { 

                        executorService.execute(new TestRunnable()); 

                        System.out.println("************* a" + i + " *************"); 

                } 

                executorService.shutdown(); 

        } 

}

class TestRunnable implements Runnable { 

        public void run() { 

                System.out.println(Thread.currentThread().getName() + "线程被调用了。"); 

                while (true) { 

                        try { 

                                Thread.sleep(5000); 

                                System.out.println(Thread.currentThread().getName()); 

                        } catch (InterruptedException e) { 

                                e.printStackTrace(); 

                        } 

                } 

        } 

}

  运行结果:

************* a0 ************* 

************* a1 ************* 

pool-1-thread-2线程被调用了。 

************* a2 ************* 

pool-1-thread-3线程被调用了。 

pool-1-thread-1线程被调用了。 

************* a3 ************* 

************* a4 ************* 

pool-1-thread-4线程被调用了。 

pool-1-thread-5线程被调用了。 

pool-1-thread-2 

pool-1-thread-1 

pool-1-thread-3 

pool-1-thread-5 

pool-1-thread-4 

pool-1-thread-2 

pool-1-thread-1 

pool-1-thread-3 

pool-1-thread-5 

pool-1-thread-4

 五、获取任务的执行的返回值

  在Java5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,但是 Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的 submit(Callable<T> task) 方法来执行,并且返回一个 <T> Future<T>,是表示任务等待完成的
Future.

  public interface Callable<V>返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。

  Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

  Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。

  Callable中的call()方法类似Runnable的run()方法,就是前者有返回值,后者没有。

  当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。

  同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null.

  遗憾的是,在Java API文档中,这块介绍的很糊涂,估计是翻译人员还没搞清楚的缘故吧。或者说是注释不到位。下面看个例子:

import java.util.ArrayList; 

import java.util.List; 

import java.util.concurrent.*;

public class CallableDemo { 

        public static void main(String[] args) { 

                ExecutorService executorService = Executors.newCachedThreadPool(); 

                List<Future<String>> resultList = new ArrayList<Future<String>>();

//创建10个任务并执行 

                for (int i = 0; i < 10; i++) { 

                        //使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中 

                        Future<String> future = executorService.submit(new TaskWithResult(i)); 

                        //将任务执行结果存储到List中 

                        resultList.add(future); 

                }

//遍历任务的结果 

                for (Future<String> fs : resultList) { 

                        try { 

                                System.out.println(fs.get());     //打印各个线程(任务)执行的结果 

                        } catch (InterruptedException e) { 

                                e.printStackTrace(); 

                        } catch (ExecutionException e) { 

                                e.printStackTrace(); 

                        } finally { 

                                //启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。 

                                executorService.shutdown(); 

                        } 

                } 

        } 

}

class TaskWithResult implements Callable<String> { 

        private int id;

public TaskWithResult(int id) { 

                this.id = id; 

        }

public String call() throws Exception { 

                System.out.println("call()方法被自动调用,干活!!!             " + Thread.currentThread().getName()); 

                //一个模拟耗时的操作 

                for (int i = 999999; i > 0; i--) ; 

                return "call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName(); 

        } 

}

  运行结果:

call()方法被自动调用,干活!!!             pool-1-thread-1 

call()方法被自动调用,干活!!!             pool-1-thread-3 

call()方法被自动调用,干活!!!             pool-1-thread-4 

call()方法被自动调用,干活!!!             pool-1-thread-6 

call()方法被自动调用,干活!!!             pool-1-thread-2 

call()方法被自动调用,干活!!!             pool-1-thread-5 

call()方法被自动调用,任务的结果是:0    pool-1-thread-1 

call()方法被自动调用,任务的结果是:1    pool-1-thread-2 

call()方法被自动调用,干活!!!             pool-1-thread-2 

call()方法被自动调用,干活!!!             pool-1-thread-6 

call()方法被自动调用,干活!!!             pool-1-thread-4 

call()方法被自动调用,任务的结果是:2    pool-1-thread-3 

call()方法被自动调用,干活!!!             pool-1-thread-3 

call()方法被自动调用,任务的结果是:3    pool-1-thread-4 

call()方法被自动调用,任务的结果是:4    pool-1-thread-5 

call()方法被自动调用,任务的结果是:5    pool-1-thread-6 

call()方法被自动调用,任务的结果是:6    pool-1-thread-2 

call()方法被自动调用,任务的结果是:7    pool-1-thread-6 

call()方法被自动调用,任务的结果是:8    pool-1-thread-4 

call()方法被自动调用,任务的结果是:9    pool-1-thread-3

Process finished with exit code 0

一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。

线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。

为了便于跨大量上下文 使用,此类提供了很多可调整的参数和扩展挂钩。但是,强烈建议程序员使用较为方便的 Executors 工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、 Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。(给力啊,转载所得)

用法示例

下面给出了一个网络服务的简单结构,这里线程池中的线程作为传入的请求。它使用了预先配置的 Executors.newFixedThreadPool(int) 工厂方法:

      class NetworkService implements Runnable {
                private final ServerSocket serverSocket;
                private final ExecutorService pool;                 public NetworkService(int port, int poolSize)
                    throws IOException {
                  serverSocket = new ServerSocket(port);
                  pool = Executors.newFixedThreadPool(poolSize);
                }
            
                public void run() { // run the service
                  try {
                    for (;;) {
                      pool.execute(new Handler(serverSocket.accept()));
                    }
                  } catch (IOException ex) {
                    pool.shutdown();
                  }
                }
              }               class Handler implements Runnable {
                private final Socket socket;
                Handler(Socket socket) { this.socket = socket; }
                public void run() {
                  // read and service request on socket
                }
             }

下列方法分两个阶段关闭 ExecutorService。第一阶段调用 shutdown 拒绝传入任务,然后调用 shutdownNow(如有必要)取消所有遗留的任务:

    void shutdownAndAwaitTermination(ExecutorService pool) {
           pool.shutdown(); // Disable new tasks from being submitted
           try {
             // Wait a while for existing tasks to terminate
             if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
               pool.shutdownNow(); // Cancel currently executing tasks
               // Wait a while for tasks to respond to being cancelled
               if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                   System.err.println("Pool did not terminate");
             }
           } catch (InterruptedException ie) {
             // (Re-)Cancel if current thread also interrupted
             pool.shutdownNow();
             // Preserve interrupt status
             Thread.currentThread().interrupt();
           }
         }

内存一致性效果:线程中向 ExecutorService 提交 RunnableCallable 任务之前的操作
happen-before 由该任务所提取的所有操作,后者依次 happen-before 通过 Future.get() 获取的结果。

http://blog.sina.com.cn/s/blog_8e6f1b330100xihy.html

ExecutorService实际上是一个线程池的管理工具的更多相关文章

  1. 线程池的管理类MyThreadPoolManager

    import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executor; import java.ut ...

  2. Android 性能优化(16)线程优化:Creating a Manager for Multiple Threads 如何创建一个线程池管理类

    Creating a Manager for Multiple Threads 1.You should also read Processes and Threads The previous le ...

  3. C#线程篇---线程池如何管理线程(6完结篇)

    C#线程基础在前几篇博文中都介绍了,现在最后来挖掘一下线程池的管理机制,也算为这个线程基础做个完结. 我们现在都知道了,线程池线程分为工作者线程和I/O线程,他们是怎么管理的? 对于Microsoft ...

  4. 死磕 java线程系列之自己动手写一个线程池(续)

    (手机横屏看源码更方便) 问题 (1)自己动手写的线程池如何支持带返回值的任务呢? (2)如果任务执行的过程中抛出异常了该怎么处理呢? 简介 上一章我们自己动手写了一个线程池,但是它是不支持带返回值的 ...

  5. 手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理

    摘要:从手写线程池开始,逐步的分析这些代码在Java的线程池中是如何实现的. 本文分享自华为云社区<手写线程池,对照学习ThreadPoolExecutor线程池实现原理!>,作者:小傅哥 ...

  6. 二 Java利用等待/通知机制实现一个线程池

    接着上一篇博客的 一Java线程的等待/通知模型 ,没有看过的建议先看一下.下面我们用等待通知机制来实现一个线程池 线程的任务就以打印一行文本来模拟耗时的任务.主要代码如下: 1  定义一个任务的接口 ...

  7. python创建一个线程和一个线程池

    创建一个线程 1.示例代码 import time import threading def task(arg): time.sleep(2) while True: num = input('> ...

  8. 死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  9. [Winform]一个简单的账户管理工具

    最近一直觉得注册的账户越来越多,帐号密码神马的容易弄混.自己就折腾了一个简单的账户管理工具,其实实现也挺简单,将每个账户的密码及相关密码提示信息,经aes算法加密之后保存到数据库,当前登录用户可以查询 ...

随机推荐

  1. IDM使用入门

    IDM使用入门 Software IDM介绍 基本使用 浏览器集成 选项设置 进阶使用 IDM+百度云外链 IDM介绍 Internet Download Manager(IDM) is a tool ...

  2. bzoj2157

    LCT板子,打个lazy即可 # include <stdio.h> # include <stdlib.h> # include <iostream> # inc ...

  3. Linux系统中svn服务器设置开机启动

    安装完svn服务器后虽然好用但是因为经常重启Linux服务器,每次重启完就要去手动启动svn服务器,很是麻烦,于是在网上找了一些方法后,自己把svn服务器设置成开机启动 步骤一:安装svn服务器: h ...

  4. 软件测试必备-前端知识点之html基础

    前端必备知识点 第一部分:HTML基础 一. web前端标准 1. 结构标签----html 2. 样式标准,美化----css 3. 行为标准---js 二. 五大浏览器厂商 1. ie 2. 谷歌 ...

  5. 解决Android Studio 3.0导入module依赖后unable to merge index

    解决Android Studio 3.0导入module依赖后unable to merge index 项目需要使用im, 在项目里导入了腾讯im的几个module依赖, 项目无法编译, 报错una ...

  6. 利用Cglib实现AOP

    前文讲了, 可以利用Spring, Guice等框架提供的容器实现AOP, 如果想绕过容器, 直接注入Class, 可以利用Cglib为对象加上动态代理,实现代码切入, 但是每次调用比较繁琐, 因此我 ...

  7. 22.jQuery(实例)

    1.开关灯效果 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  8. 为你的WordPress博客添加CSS3炫酷读者墙

    为你的WordPress博客添加CSS3炫酷读者墙,也就是把你文章的评论最活跃的读者显示在单独的一个页面,先看看效果吧: 1.复制主题的page.php,另存为readerwall.php,然后在其顶 ...

  9. 用jquery实现日期控件

    用jquery实现的日期控件,代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta ch ...

  10. Linux中断子系统:级联中断控制器驱动

    Linux中断子系统 Linux中断子系统是个很大的话题,如下面的思维导图所示,包含硬件.驱动.中断上半部.中断下半部等等.本文着眼于中断控制器(PIC),特别是级联中断控制器驱动部分,对驱动的设计和 ...