• 【高级JSE技术】线程池


    引言

    线程池是java并发技术中常用的一种,通过缓存线程资源避免了频繁的创建销毁线程带来的额外开销,从而提高性能。JDK中有相应的Executor框架实现,而Spring中也带有线程池的功能。本文将从以下几个角度来梳理线程池的用法和注意点:

    1. Executor框架
      1. 种类和特点
      2. Callable和Future
      3. 使用实例
    2. 使用Spring线程池

    Executor框架

    种类和特点

    Java的Executor框架是专门设计用在以任务为单位的并发情景下的。以任务为单位的含义是:每次请求与请求之间互不关联,请求的结果不受其他请求的影响。这样可以将每个请求封装成一个个任务,交给Executor框架执行。举个例子,递归程序能否用Executor框架进行优化? 事实上一些递归程序的确可以用Executor框架进行优化,比如说归并排序,在合并阶段就可以并发的合并得到结果。但是某些递归程序就无法使用Executor来优化,比如说斐波那契求解,因为后一个结果需要依赖前一个结果,这样的任务是不独立的,无法切割。说了这么多,下面来看一个简单的使用例子:

    public class Executor {

    public static void main(String[] args) {

    Executor executor = Executors.newFixedThreadPool(10);

    Runnable task = new Runnable() {

    @Override

    public void run() {

    System.out.println("task over");

    }

    };

    executor.execute(task);

    executor = Executors.newScheduledThreadPool(10);

    ScheduledExecutorService scheduler = (ScheduledExecutorService) executor;

    scheduler.scheduleAtFixedRate(task, 10, 10, TimeUnit.SECONDS);

    }

    }

    其中 Executors是Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, 和 Callable的工厂。他提供一系列的静态方法来获取特定的线程池,这些线程池的功能各有特点,下面我们来看一下Executors的OutLine:

    我们可以看到Executors提供了大概以下这么几种创建线程池的方法:

    1. newFixThreadPool:创建一个固定大小的线程池,每次提交一个任务就生成一个线程,直到最大值,这时候如果某个线程发生Exception挂掉了再生成一个新的补上。
    2. newSingleThreadExecutor:只创建一个线程来执行任务,如果这个线程挂了则再次生成一个新的线程。这个线程池可以保证任务被串行的执行,适用于一些特殊的场景,如FIFO,优先级等。
    3. newCachedThreadPool:创建一个带缓冲的线程池,如果线程池的资源有剩余则回收,如果线程池资源紧张则再次添加。
    4. newScheduledThreadPool:创建一个固定大小的线程池,所有任务都是以定时或延时的方式进行的,类似Timer。

    Callable和Future

    Callable是一种和runable不同的任务接口,主要用于解决任务执行后需要放回结果的问题。从上面第一个例子我们可以看到runable的任务执行完后是没有返回结果的,这样在某些场景下是不适用的,比如说我需要存储100张图片,在存储的过程中可能会抛出异常导致存储失败。那么我需要在任务重捕获异常并且标记当前编号的图片存储失败,并且返回一个失败的标记。这时候就需要我们使用callable而不是runable。下面我们来看一个例子:

    class TaskWithResult implements Callable<String> {

        private int id;

        public TaskWithResult(int id) {

            this.id = id;

        }

        @Override

        public String call() throws Exception {

            return "result of TaskWithResult " + id;

        }

    }

    public class CallableTest {

        public static void main(String[] args) throws InterruptedException,

                ExecutionException {

            ExecutorService exec = Executors.newCachedThreadPool();

            ArrayList<Future<String>> results = new ArrayList<Future<String>>();

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

                results.add(exec.submit(new TaskWithResult(i)));

            for (Future<String> fs : results) {

                if (fs.isDone())

                    System.out.println(fs.get());

                else

                    System.out.println("Future result is not yet complete");

            }

            exec.shutdown();

        }

    }

    上面的代码首先是TaskWithResult实现callable的接口,重写了call方法,返回一段字符串。Main里面生成了一个CachedThreadPool,下面这句是重点,results.add(exec.submit(new TaskWithResult(i))) 它做了三件事,一生成一个callable的任务,二提交给线程池,三将结果加入results队列。Results是一个future类型的队列,什么事future类型呢?简单来讲future 相当于是用来存放Executor执行的结果的一种泛型容器,上述例子中我们生成了一个String类型的结果容器,所以要求返回的结果是String类型的。

     

     

     

     

     

     

  • 相关阅读:
    xCode中怎样保存自己的代码块
    2015-03-13---抽象工厂(附代码),
    java nio 缓冲区(一)
    MFC获取各个窗体(体)之间的指针(对象)
    自己动手写神经网络,自己真的能够动手写神经网络嘛?
    Android招財进宝手势password的实现
    QQ三方登录
    UVA 10561
    Vagi单点登录1.0
    《反脆弱》:软件业现成的鲁棒性(Robust)换了个说法变成了作者的发明,按作者的理论推导出许多可笑愚蠢的原则来
  • 原文地址:https://www.cnblogs.com/AlwaysFixBug/p/4812551.html
Copyright © 2020-2023  润新知