线程池初步认识

来源:互联网 发布:快期模拟交易软件 编辑:程序博客网 时间:2024/05/05 23:25

1.什么是线程池
为了避免系统频繁地创建和销毁线程,消耗系统资源,我们可以让创建的线程进行复用。就如同数据库的连接池一样,当系统需要用到数据库时,并不是创建一个新的连接,而是从连接池中获取一个连接。线程也类似,当需要用到线程时就可以去池子中拿一个空闲线程,用完之后就“还”给线程池,通过这种方式可以节约不少创建和销毁线程的时间。
2.JDK对线程池的支持
Executor结构图:
这里写图片描述
其中ThreadPoolExecutor表示一个线程池,Executors类则扮演这线程池工厂的角色。通过这个工厂可以获得一下类型的线程池:

public static ExecutorService newFixedThreadPool(int nThreads);public static ExecutorService newSingleThreadExecutor();public static ExecutorService newCachedThreadPool();public static ScheduledExecutorService newSingleThreadScheduledExecutor();public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);

1.newFixedThreadPool()方法:该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有则新的任务会被暂存在一个任务队列中,待有线程空闲时在执行。
2.newSingleThreadExecutor()方法:该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
3.newCachedThreadPool()方法:该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
4.newSingleThreadScheduledExecutor()方法:该方法返回一个ScheduledExecutorService对象,线程池大小为1。ScheduledExecutorService接口在ExecutorService接口上扩展了在给定时间执行某任务的功能。
5.newScheduledThreadPool()方法:和newSingleThreadScheduledExecutor()方法不同的是该线程池可以指定线程数量。
2.1计划任务
与其他几个线程池不同,ScheduledExecutorService并不会一定会立即安排执行任务。它其实是起到了计划任务的作用。它提供了一下几个方法

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);public ScheduledFuture<?> shceduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

方法schedule()会在给定时间,对任务进行一次调度。方法shceduleAtFixedRate和方法scheduleWithFixedDelay会对任务进行周期性调度。对于FixedRate来说,任务调度的频率是一定的,它是以上一个任务开始执行时间为起点,之后的period时间调度下一次任务。而FixDelay则是在上一个任务结束后,再经过Delay时间进行任务调度。
2.2核心线程池的内部实现
线程池均使用了ThreadPoolExecutor实现。以下是三个线程池的实现方法:

public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());    }public static ExecutorService newSingleThreadExecutor() {        return new FinalizableDelegatedExecutorService            (new ThreadPoolExecutor(1, 1,                                    0L, TimeUnit.MILLISECONDS,                                    new LinkedBlockingQueue<Runnable>()));    } public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue<Runnable>());    }

从以上代码实现来看,他们只是ThreadPoolExecutor。下面是其最重要的构造函数

 public ThreadPoolExecutor(int corePoolSize,  //指定线程池中的线程数量                              int maximumPoolSize,  //指定线程池中的最大线程数量                              long keepAliveTime,  //当线程数量超过corePoolSize时,多余空闲线程的存活时间                              TimeUnit unit,  //存活时间的单位                              BlockingQueue<Runnable> workQueue,  //任务队列,被提交但尚未被执行的任务                              ThreadFactory threadFactory,  //线程工厂,用于创建线程,一般为默认                              RejectedExecutionHandler handler);  //拒绝策略,当任务太多来不及处理,如何拒绝任务

参数workQueue任务队列,是一个BlockingQueue接口的对象,用于存放Runnable对象。根据队列功能的分类,可使用以下几种BlockingQueue:
1.直接提交队列:由SynchronousQueue对象提供。它是一种特殊的BlockingQueue。SynchronousQueue没有容量,每一个插入操作对应着一个删除操作,反之也一样。提交的任务不会被真实保存,而是将新任务提交给线程执行。
2.有界任务队列:可以使用ArrayBlockingQueue实现。若有新的任务需要执行时,如果线程池的实际线程数小于CorePoolSize,则会优先创建新的线程,若大于CorePoolSize,则会将新的任务加入等待队列。若队列已满,无法加入,则在总线程数不大于maximumPoolSize的前提下,创建新的进程执行任务。若大于,则执行拒绝策略。
3.无解任务队列:通过LinkedBlockingQueue类实现。与有界队列相比,除非资源耗尽,否则无界的任务队列不存在任务入队失败的情况。
4.优先任务队列:优先任务队列是带有执行优先级的队列。通过PriorityBlockingQueue实现,可以控制任务的执行先后顺序,是一种特殊的无解队列。
2.3.1拒绝策略
ThreadPoolExecutor的最后一个参数是拒绝策略。当系统超负荷运行时就会执行拒绝策略,JDK内置的拒绝策略如下:
1.AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
2.CallerRunsPolicy策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是任务提交线程的性能极有可能会急剧下降。
3.DiscardOledestPolicy策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
4.DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理。
以上内置的策略均实现的RejectedExecutionHandler接口,当然也可以自己扩展接口,自定义自己的策略。接口定义如下:

public interface RejectedExecutionHandler{    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);}

2.3.2自定义线程创建工厂:ThreadFactory
ThreadFactory是一个接口,只有一个方法用于创建线程,我们可以实现这个接口,自定义线程创建工厂:

Thread newThread(Runnable r);

3.扩展线程池
ThreadPoolExecutor是一个可以扩展的线程池。它提供了beforExecute(),afterExecute(),terminated()三个接口线程池进程扩展,线程池扩展对线程池运行状态的跟踪,输出一些有用的调试信息以帮助系统故障诊断,如:

package test;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledFuture;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ExtThreadPool {    public static class MyTask implements Runnable{        public String name;        public MyTask(String name) {            // TODO Auto-generated constructor stub            this.name =  name;        }        @Override        public void run() {            // TODO Auto-generated method stub            System.out.println("正在执行"+":Thread Id:"+Thread.currentThread().getId()+",Task Name="+name);            try {                Thread.sleep(100);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }    public static void main(String[] args) throws InterruptedException {        ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,                 new LinkedBlockingQueue<Runnable>())                {                    @Override                    protected void beforeExecute(Thread t, Runnable r) {                        System.out.println("准备执行: "+((MyTask)r).name);                    }                    @Override                    protected void afterExecute(Runnable r, Throwable t) {                        System.out.println("执行完成: "+((MyTask)r).name);                    }                    @Override                    protected void terminated() {                        System.out.println("线程退出");                    }                };                for(int i=0; i<5; i++)                {                    MyTask task = new MyTask("TASK-GEYM-"+i);                    es.execute(task);                    Thread.sleep(10);                }                es.shutdown();    }}

4.优化线程池线程数量
计算公式如下:
这里写图片描述

0 0
原创粉丝点击