大多数并发都是通过任务执行的方式来实现的。一般有两种方式执行任务:串行和并行。

class SingleThreadWebServer {
    public static void main(String[] args) throws Exception {
        ServerSocket socket = new ServerSocket(80);
        while(true) {
            Socket conn = socket.accept();
            handleRequest(conn);
        }
    }
}

class ThreadPerTaskWebServer {
    public static void main(String[] args) throws Exception {
        ServerSocket socket = new ServerSocket(80);
        while(true) {
            final Socket conn = socket.accept();
            Runnable task = new Runnable() {
                public void run() {
                    handleRequest(conn);
                }
            };
            new Thread(task).start();
        }
    }
}

当然上面的这两种方式都是有问题的。单线程的问题就是并发量会是瓶颈,多线程版本就是无限制的创建线程会导致资源不足问题。

Executor 框架

任务是一组逻辑工作单元,而线程是使任务异步执行的机制。

JDK 提供了 Executor 接口:

public interface Executor {
    void execute(Runnable command);
}

虽然 Executor 接口比较简单,但是却是异步任务执行框架的基础,该框架能支持多种不同类型的任务执行策略。它提供了一种标准的方式把任务的提交过程与执行过程进行了解耦。用 Runnable 来代表任务。Executor 的实现提供了对生命周期的支持以及统计信息应用程序管理等机制。

Executor 是基于生产者消费者模式的,提交任务的操作相当于生产者,执行任务的线程相当于消费。

基于 Executor 的 WebServer 例子如下:

public class TaskExecutorWebServer {
    private static final int NTHREADS = 100;
    private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(80);
        while (true) {
            final Socket conn = serverSocket.accept();
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    handleRequest(conn);
                }
            };
            exec.execute(task);
        }
    }
}

另外可以自己实现 Executor 来控制是并发还是并行的,如下面代码:

/**
 * 执行已提交的 Runnable 任务的对象。
 * 此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。
 * 通常使用 Executor 而不是显式地创建线程。
 *
 *
 * @author renchunxiao
 *
 */
public class ExecutorDemo {
    public static void main(String[] args) {
        Executor executor = new ThreadExecutor();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                // do something
            }
        });

        Executor executor2 = new SerialExecutor();
        executor2.execute(new Runnable() {
            @Override
            public void run() {
                // do something
            }
        });

    }
}

/**
 * 创建一个线程来执行 command
 *
 * @author renchunxiao
 *
 */
class ThreadExecutor implements Executor {
    @Override
    public void execute(Runnable command) {
        new Thread(command).start();
    }
}

/**
 * 串行执行 command
 *
 * @author renchunxiao
 *
 */
class SerialExecutor implements Executor {
    @Override
    public void execute(Runnable command) {
        command.run();
    }
}

线程池

线程池就是线程的资源池,可以通过 Executors 中的静态工厂方法来创建线程池。

  • newFixedThreadPool。创建固定长度的线程池,每次提交任务创建一个线程,直到达到线程池的最大数量,线程池的大小不再变化。
  • newSingleThreadExecutor。单个线程池。
  • newCachedThreadPool。根据任务规模变动的线程池。
  • newScheduledThreadPool。创建固定长度的线程池,以延迟或定时的方式来执行任务。

JVM 只有在所有非守护线程全部终止后才会退出,所以,如果无法正确的关闭 Executor,那么 JVM 就无法结束。

为了解决执行服务的生命周期问题,有个扩展 Executor 接口的新接口 ExecutorService。

public interface ExecutorService extends Executor {

    void shutdown();

    List<Runnable> shutdownNow();

    boolean isShutdown();

    boolean isTerminated();

    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> Future<T> submit(Callable<T> task);

    <T> Future<T> submit(Runnable task, T result);

    Future<?> submit(Runnable task);

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

ExecutorService 生命周期有三种状态:运行、关闭、已终止。ExecutorService 在初始创建时处于运行状态。shutdown 方法会平缓关闭:不在接受新的任务,并且等待已经执行的任务执行完成(包括那些还未开始的任务)。shutdownNow 方法将粗暴关闭:它将尝试取消所有运行中的任务,并且不再启动队列中尚未开始的任务。所有任务都执行完成后进入到已终止状态。

Callable 和 Future

Executor 框架使用 Runnable 作为基本的任务表示形式。Runnable 是一种有局限性的抽象,它的 run 方法不能返回值和抛出一个受检查异常。

许多任务实际上是存在延时的计算,例如数据库查询,从网络获取资源。对于这些任务,Callable 是更好的抽象,它认为 call 将返回一个值,并且可能抛出异常。

Executor 执行的任务有四个生命周期阶段:创建、提交、开始和完成。由于有些任务需要很长时间有可能希望取消,在 Executor 框架当中,已提交未开始的任务可以取消。

Future 表示一个任务的生命周期,并且提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。

Java Executor 框架学习总结的更多相关文章

  1. Java Executor 框架

    Java Executor 框架 Executor框架是指java5中引入的一系列并发库中与executor相关的功能类,包括Executor.Executors. ExecutorService.C ...

  2. Java Executor框架使用

    Java Executor框架是Jdk1.5之后推出的,是为了更加方便的开发多线程应用而封装的框架: 相比传统的Thread类,Java Executor使用方便,性能更好,更易于管理,而且支持线程池 ...

  3. JAVA并行框架学习之ForkJoin

    当硬件处理能力不能按照摩尔定律垂直发展的时候,选择了水平发展,多核处理器已经广泛应用.未来随着技术的进一步发展,可能出现成百上千个处理核心,但现有的程序运行在多核心处理器上并不能得到较大性能的提升,主 ...

  4. Executor框架学习笔记

    Java中的线程即是工作单元也是执行机制,从JDK 5后,工作单元与执行机制被分离.工作单元包括Runnable和Callable,执行机制由JDK 5中增加的java.util.concurrent ...

  5. Java集合框架学习(一)List

    先附一张Java集合框架图. 从上面的集合框架图可以看到,Java集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射.Coll ...

  6. Java Executor框架

    java.util.concurrent 包中包含灵活的线程池实现,但是更重要的是,它包含用于管理实现 Runnable 的任务的执行的整个框架,该框架称为 Executor 框架.该框架基于生产者- ...

  7. 使用Java Executor框架实现多线程

    本文将涵盖两个主题: 通过实现Callable接口创建线程 在Java中使用Executor框架 实现Callable接口 为了创建一段可以在线程中运行的代码,我们创建了一个类,然后实现了Callab ...

  8. Java集合框架学习

    集合框架 集合框架的目标 该框架必须是高性能的.基本集合(动态数组,链表,树,哈希表)的实现必须是高效的. 该框架允许 不同类型的集合,以类似的方式工作,具有高度的互操作性. 对一个集合的扩展和适应必 ...

  9. Java SSH框架学习(入门)

    SSH就是 struts+spring+hibernate 的一个集成框架,是java中一种流行的JAVA WEB 应用程序开源框架.由于我最熟悉的还是ASP.NET的的MVC和Python的Djan ...

随机推荐

  1. CoreAnimation实现一个折线表

    将折现表封装到一个view里,暴露给使用者的只有一个传入数据的方法. // // ChartLine.h // BoxingChampion //功能:根据传入的数组,绘制折线图 注意 其frame的 ...

  2. 用原生js实现一个页面乘法口诀表

    今天我自己用js实现了一个页面乘法口诀表(如图)来共享给大家,做的不是很好,如果大家有新的想法可以跟我交流哦. 代码如下: <!doctype html><html lang=&qu ...

  3. 【转】ASP.NET MVC教程

    转自:http://www.cnblogs.com/QLeelulu/category/123326.html ASP.NET MVC的最佳实践与性能优化的文章 摘要: 就一些文章链接,就不多废话了. ...

  4. 获取动态SQL查询语句返回值(sp_executesql)

    在写存储过程时经常会遇到需要拼接SQL语句的情况,一般情况下仅仅是为了执行拼接后的语句使用exec(@sql)即可. 而今天的一个存储过程却需要获取动态SQL的查询结果. 需求描述:在某表中根据Id值 ...

  5. 子网/ip/子网掩码

    IP地址由网络地址和主机地址组成 而现在IP由“子网掩码”通过子网网络地 址细分出 A,B,C类更小的网络.这种方式 实际上就是将原来的A类,B类,C类等分类 中的的主机地址部分用作子网地址,可以 将 ...

  6. #Leet Code# Root to leaf

    语言:Python 描述:使用递归实现 def getList(self, node): if node is None: return [] if node.left is None and nod ...

  7. python第一次上机遇到的困难

      正确 10 58 27412 2-1019 长度转换程序(10分) 完善下面的程序,能够: (1) 将用户输入的公制长度单位(米.千米)转换成英制长度单位(英寸.英里): (2) 将用户输入的英制 ...

  8. IntelIoT技术笔记Maven

    1.Maven project facet配置 错误信息: One or more constraints have not been satisfied.以及Cannot change versio ...

  9. 有关collection中的一些数据结构

    Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).一些Collection允许相同的元素 ...

  10. 去除UINavigationBar默认透明度的方法

    UINavigationbar的属性translucent,用来控制导航条的透明度的: iOS7+版本后,navigationbar的translucent属性默认为YES,及默认带有透明度 [sel ...