java线程池

来源:互联网 发布:b超三个数据看男女技巧 编辑:程序博客网 时间:2024/06/05 19:15

1 线程池简介
线程池,从字面意思来看,是指管理一组同构工作线程的资源池。线程池是与工作队列密切相关的,其中在工作队列中保存了所有等待执行的任务。工作者线程的任务很简单:从工作队列中获得一个任务,执行任务,然后返回线程池并且等待下一个任务。
2 线程池优势
“在线程池中执行任务”比“为每一个任务分配一个线程”优势更多。通过重用现有的线程,而不是创建新的线程,可以在处理多个请求的时候分摊在线程创建,销毁过程中产生的巨大开销。另外一个好处是,当请求到达时,工作线程通常已经存在,因此不会由于等待创建线程而延迟任务的执行,从而提高了响应性。通过创建足够多的线程以保持忙碌状态,同时还可以防止多线程互相竞争资源而使得内存耗尽。
3 4种java常用线程池介绍
1 newFixedThreadPool 将创建一个固定长度的线程池,每提交一个任务就会创建一个线程直到达到线程池的最大值。此时任务会在一个队列中等待,直到有空闲的线程来处理这个任务。
2 newCacheThreadPool 将创建一个可以缓存的线程池,如果线程池的当前规模超过了任务量时,将回收空闲线程,而当线程数量不足时将会添加新的线程。线程池的规模不受到线程。
3 newSingleThreadPool 是一个单线程的线程池,它创建当个工作者线程来执行任务,如果这个线程异常结束,会创建另外一个线程来代替,该线程池可以确保任务的顺序执行。
4 newScheduledThreadPool 创建一个固定长度的线程池,而且以延迟或者定时的方式来执行。类似于Timer。
4 线程池的生命周期
我们已经知道如何创建一个线程池,但是却没有讨论如何关闭它。由于线程池是以异步的方式来执行任务的,因此在任何时候,之前提交任务的状态是不可见的。有些任务已经完成,有些可能正在运行,而有些可能正在队列中等待。当关闭线程池的时候,需要考虑线程池当中的线程。我们可以用一种比较平缓的方式进行关闭,也可以暴力关闭。何谓平缓,即不再接受新的任务,且等待当前所有任务执行完才关闭线程池。何谓暴力,就像拉电闸一样,不考虑任务状况,直接关闭。Executors提供了关闭线程池的接口
pool.shotdown() 平缓关闭
pool.shotdownNow() 暴力关闭
5 谁先完成任务就处理谁
在日常开发中可能碰到这么一种需求,即多个任务同时执行,一旦有一个任务完成了就处理某个任务。如果用轮询的方式来处理这个问题,很显然效率低下。jdk提供了工具类专门处理这种问题,下面通过一个demo来介绍;

package com.wsy.thread;import java.util.concurrent.Callable;import java.util.concurrent.CompletionService;import java.util.concurrent.ExecutorCompletionService;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;/** * 完成服务 * 假设:我们有一组任务,谁先完成我们就获得谁。就可以使用完成服务 * @author shuyweng * */public class CompletionServiceDemo {    private static final ExecutorService pool = Executors.newFixedThreadPool(10);    public static void main(String[] args) {        // 将线程池当做参数传入完成服务~        CompletionService<String> completionService = new ExecutorCompletionService<String>(pool);        // 将任务交给completionService处理        completionService.submit(new Callable<String>() {            @Override            public String call() throws Exception {                // 睡眠一个随机的时间,模拟完成任务的随机场景                Thread.currentThread().sleep((long)Math.random()*10000);                return "123";            }        });        try{            // 一旦有某个任务处理完成,就会获得一个future。            Future<String> future = completionService.take();            String rs = future.get();            System.out.println("处理结果:" + rs);        }catch(Exception e){            e.printStackTrace();        }    }}

这个demo的好处在于,碰到类似的问题的时候不需要再通过队列来编写这个逻辑。
6 如何为任务设置时限
有时候,如果某个任务无法在指定的时间内完成,我们将不再需要它。比如一个web应用程序从外部系统获取广告,假如没有获得就使用本系统的默认广告。类似的需求还有很多,所以我们要会处理这种时限问题。
String rs = future.get(10, TimeUnit.SECONDS);
其中10代表时间的大小,TimeUnit.SECONDS代表时间单位。那么这个future就会在等待一定时间后而放弃等待。
预定时间方法很容易扩展到任意数量的任务上。考虑这样一个旅行预定门户网站:用户输入旅行的日期和其他要求,门户网站获取并且显示来自多条航线,旅店或者汽车租赁公司的报价。在获取不同公司报价的过程当中,可能会调用web服务,访问数据库,执行一个事物或者其他机制。在这种情况下,不宜让页面的响应时间受限于最慢的响应时间,而应该只显示在指定时间内收到的信息。对于没有及时响应的服务提供者,页面可以忽略他们,或者提示一个提示信息,比如:“未能在指定时间内获得报价!”。
很显然从一个公司获得报价的过程与从其他公司获得报价的过程无关,因此可以将获得报价的过程当成一个任务,从而使获得报价的过程能并发得执行。创建n个任务,将其提交到一个线程池,保留n个future,并使用限时的get方法从Future串行地获得每一个结果,这一切都很简单。但是还有一个更加简单的方式:invokeAll。
invokeAll可以让我们不用对每一个future都写一个定时的任务,当invokeAll返回后,每个任务要么正常地完成,要么被取消,所以客户端可以使用get或者isCanceled来判断究竟何种情况?

List<Future<T>> futures = exec.invokeAll(tasks, time, unit);

其中tasks代表一个任务的集合,time表示时间,unit表示时间单位

原创粉丝点击