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

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. 类库探源——System.Exception

    一.MSDN描述 Exception 类: 表示在应用程序执行期间发生的错误 命名空间 : System 程序集:   mscorlib.dll 继承关系: 常用属性(含字段)和方法: 1. 属性Me ...

  2. java经典题目练习-第八题简单实现方式...

    *[程序8]*题目:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字.* 例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制. 思考: 对于以上 ...

  3. 翻纸牌 高校俱乐部 英雄会 csdn

    题目描述 有一种纸牌游戏,很有意思,给你N张纸牌,一字排开,纸牌有正反两面,开始的纸牌可能是一种乱的状态(有些朝正,有些朝反),现在你需要整理这些纸牌.但是麻烦的是,每当你翻一张纸牌(由正翻到反,或者 ...

  4. Nginx 独立图片服务器的搭建

    为什么需要独立图片服务器? 如果你留心的话,可以发现,现在主流的网站都是有单独的图片服务器的,例如,人人网的为rrimg,淘宝的为taobaocdn,下面还有很多的二级域名. 独立的图片服务器有诸多好 ...

  5. TCP与UDP网络编程总结(一)

    (1):TCP网络编程 我们注意到服务端与客户端通信时是通过客户端的套接字相互通信的,那么服务端的套接字主要是干什么用的呢? TCP服务端设置监听套接字时 int listen(int sock,in ...

  6. c#写个基础的Socket通讯

    晚上想写点东西,想想把我刚来公司学的Sockt通讯写上来吧.要写的简单易懂点,新人们可以借鉴下哦,用控制台写. 先得说说Socket,与TCP/UDP啥关系,一直讲什么Socket通讯,TCP通讯,都 ...

  7. ubuntu下mysql安装与测试

    原文地址: http://www.cnblogs.com/zhuyp1015/p/3561470.html 注意:原文地址中,最后g++ 编译源代码时少了个字母.添上即可. ubuntu上安装mysq ...

  8. Android 开发遇到的问题及解决办法

    Failed to resolve: com.android.support:appcompat-v7:23.4.0 问题解决办法: 1.在Android SDK Manager中找到对应的SDK版本 ...

  9. 大话设计模式之策略模式(strategy)

    策略模式:它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响使用算法的用户. 针对商城收银模式,打折,返现促销等的例子: 打折还是促销其实都是一些算法,可以用工厂模式来 ...

  10. Welcome to JimmyCheung's blog!

    博客开通,写写学习笔记,写写心情,写写生活中的点点滴滴~ 有钱的捧个钱场嘞,没钱的贡献个点击量,新鲜的博客出炉咯,来五毛钱的博文呗~ By Jimmy 2014.09.16