Java线程池的使用

来源:互联网 发布:吸烟有害健康烟盒 知乎 编辑:程序博客网 时间:2024/06/03 12:28

在Java1.5中提供了一个非常高效实用的多线程包:java.util.concurrent,提供了大量高级工具,可以帮助开发者编写高效易维护、结构清晰的Java多线程程序。

线程池

之前我们在使用多线程都是用Thread的start()来创建启动一个线程,但是在实际开发中,如果每个请求到达就创建一个新线程,开销是相当大的。服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。这就引入了线程池概念。

线程池的原理其实就是对多线程的一个管理,为了实现异步机制的一种方法,其实就是多个线程执行多个任务,最终这些线程通过线程池进行管理…不用手动去维护…一次可以处理多个任务,这样就可以迅速的进行相应…比如说一个网站成为了热点网站,那么对于大量的点击量,就必须要对每一次的点击做出迅速的处理,这样才能达到更好的交互效果…这样就需要多个线程去处理这些请求,以便能够更好的提供服务…

在java.util.concurrent包下,提供了一系列与线程池相关的类。合理的使用线程池,可以带来多个好处:

(1) 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗;

(2) 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行;

(3) 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池可以应对突然大爆发量的访问,通过有限个固定线程为大量的操作服务,减少创建和销毁线程所需的时间。

使用线程池:

  • 1、创建线程池

  • 2、创建任务

  • 3、执行任务

  • 4、关闭线程池

创建线程池

一般通过工具类Executors的静态方法来获取线程池或静态方法。介绍四种常用创建方法

ExecutorService service1 = Executors.newSingleThreadExecutor();

说明: 单例线程,表示在任意的时间段内,线程池中只有一个线程在工作

ExecutorService service2 = Executors.newCacheThreadPool();

说明: 缓存线程池,先查看线程池中是否有当前执行线程的缓存,如果有就resue(复用),如果没有,那么需要创建一个线程来完成当前的调用.并且这类线程池只能完成一些生存期很短的一些任务.并且这类线程池内部规定能resue(复用)的线程,空闲的时间不能超过60s,一旦超过了60s,就会被移出线程池

ExecutorService service3 = Executors.newFixedThreadPool(10);

说明: 固定型线程池,和newCacheThreadPool()差不多,也能够实现resue(复用),但是这个池子规定了线程的最大数量,也就是说当池子有空闲时,那么新的任务将会在空闲线程中被执行,一旦线程池内的线程都在进行工作,那么新的任务就必须等待线程池有空闲的时候才能够进入线程池,其他的任务继续排队等待.这类池子没有规定其空闲的时间到底有多长.这一类的池子更适用于服务器.

ExecutorService service4 = Executors.newScheduledThreadPool(10);

说明: 调度型线程池,调度型线程池会根据Scheduled(任务列表)进行延迟执行,或者是进行周期性的执行.适用于一些周期性的工作.

package com.reapal.brave.main;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * Created by jack-cooper on 2017/2/23. */public class Test {    public static void main(String[] args) {        ExecutorService service = Executors.newCachedThreadPool();        service.submit(new Runnable() {            @Override            public void run() {                while(true){                    System.out.println("hello world !");                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        System.out.println(" ===> main Thread execute here ! " );    }}

创建任务

任务分为两种:一种是有返回值的( callable ),一种是没有返回值的( runnable ). Callable与 Future 两功能是Java在后续版本中为了适应多并法才加入的,Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务。

  • 无返回值的任务就是一个实现了runnable接口的类.使用run方法.
  • 有返回值的任务是一个实现了callable接口的类.使用call方法.

Callable和Runnable的区别如下:

  • Callable定义的方法是call,而Runnable定义的方法是run。
  • Callable的call方法可以有返回值,而Runnable的run方法不能有返回值。
  • Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常。

Future 介绍

Future表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。Future的cancel方法可以取消任务的执行,它有一布尔参数,参数为 true 表示立即中断任务的执行,参数为 false 表示允许正在运行的任务运行完成。Future的 get 方法等待计算完成,获取计算结果。

package com.reapal.brave.main;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class CallableAndFuture {    public static class  MyCallable  implements Callable{        private int flag = 0;        public MyCallable(int flag){            this.flag = flag;        }        public String call() throws Exception{            if (this.flag == 0){                return "flag = 0";            }            if (this.flag == 1){                try {                    while (true) {                        System.out.println("looping.");                        Thread.sleep(2000);                    }                } catch (InterruptedException e) {                    System.out.println("Interrupted");                }                return "false";            } else {                throw new Exception("Bad flag value!");            }        }    }    public static void main(String[] args) {        // 定义3个Callable类型的任务        MyCallable task1 = new MyCallable(0);        MyCallable task2 = new MyCallable(1);        MyCallable task3 = new MyCallable(2);        // 创建一个执行任务的服务        ExecutorService es = Executors.newFixedThreadPool(3);        try {            // 提交并执行任务,任务启动时返回了一个Future对象,            // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作            Future future1 = es.submit(task1);            // 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行            System.out.println("task1: " + future1.get());            Future future2 = es.submit(task2);            // 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环            Thread.sleep(5000);            System.out.println("task2 cancel: " + future2.cancel(true));            // 获取第三个任务的输出,因为执行第三个任务会引起异常            // 所以下面的语句将引起异常的抛出            Future future3 = es.submit(task3);            System.out.println("task3: " + future3.get());        } catch (Exception e){            System.out.println(e.toString());        }        // 停止任务执行服务        es.shutdownNow();    }}

执行任务

通过java.util.concurrent.ExecutorService接口对象来执行任务,该对象有两个方法可以执行任务execute和submit。execute这种方式提交没有返回值,也就不能判断是否执行成功。submit这种方式它会返回一个Future对象,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成。

execute与submit区别:

  • 接收的参数不一样
  • submit有返回值,而execute没有
  • submit方便Exception处理
  • execute是Executor接口中唯一定义的方法;submit是ExecutorService(该接口继承Executor)中定义的方法

关闭线程池

线程池使用完毕,需要对其进行关闭,有两种方法

shutdown()

说明:shutdown并不是直接关闭线程池,而是不再接受新的任务…如果线程池内有任务,那么把这些任务执行完毕后,关闭线程池

shutdownNow()

说明:这个方法表示不再接受新的任务,并把任务队列中的任务直接移出掉,如果有正在执行的,尝试进行停止



作者:jackcooper
链接:http://cdn2.jianshu.io/p/edd7cb4eafa0
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
原创粉丝点击