线程池

来源:互联网 发布:分词软件 编辑:程序博客网 时间:2024/05/18 01:39

1.为什么使用线程池

⑴避免创建和销毁线程的系统开销

⑵控制最大并发数,防止阻塞

⑶对线程进行简单管理,如延迟执行

2.Java中,线程池的概念是Executor这个接口,具体实现为ThreadPoolExecutor类,ThreadPoolExecutor提供四个构造函数。

//五个参数的构造函数public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue)//六个参数的构造函数-1public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue,                          ThreadFactory threadFactory)//六个参数的构造函数-2public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue,                          RejectedExecutionHandler handler)//七个参数的构造函数public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue,                          ThreadFactory threadFactory,                          RejectedExecutionHandler handler)  

如下是对他们的解释:

⑴corePoolSize

新建线程时如果corePoolSize,则新建的是核心线程,默认情况下会一直存在于线程池中,如果指定ThreadPoolExecutorallowCoreThreadTimeOut这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中.

int maximumPoolSize

该线程池中线程总数最大值。线程总数 =核心线程数+非核心线程数

long keepAliveTime

非核心线程闲置超时时长一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉,如果指定ThreadPoolExecutorallowCoreThreadTimeOut这个属性为true,那么它也会起作用,直到核心线程为0.

TimeUnit unit

keepAliveTime的单位,TimeUnit是一个枚举类型,其包括:

NANOSECONDS 1微毫秒= 1微秒/ 1000

MICROSECONDS 1微秒= 1毫秒/ 1000

MILLISECONDS 1毫秒= 1/1000

SECONDS : 秒

MINUTES : 分

HOURS : 小时

DAYS : 天

⑸BlockingQueue<Runnable> workQueue

该线程池中的任务队列:维护着等待执行的Runnable对象

当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务

常用的workQueue类型:

①SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限

②LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize

③ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

④DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

ThreadFactory threadFactory

创建线程的方式,这是一个接口,你new他的时候需要实现他的Thread newThread(Runnable r)方法,一般用不上

RejectedExecutionHandler handler

这玩意儿就是抛出异常专用的,不指定也有默认的,一般用不上。

3.ThreadPoolExecutor四个重要方法:

execute()    //可以向线程池提交一个任务,交由线程池处理submit()     //也是用来向线程池提交任务,和execute()比可以返回任务执行的结果,实际上就是调用execute()方法,利用Future获取结果shutdown()      //关闭线程池shutdownNow()   //关闭线程池
4.向ThreadPoolExecutor添加任务

⑴通过ThreadPoolExecutor.execute(Runnable command)方法即可向线程池内添加一个任务

⑵ThreadPoolExecutor的策略

当一个任务被添加进线程池时:

线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务

线程数量达到了corePools,则将任务移入队列等待

队列已满,新建线程(非核心线程)执行任务

队列已满,总线程数又达到了maximumPoolSize,就会由上面那位星期天(RejectedExecutionHandler)抛出异常

5.线程池的原理

主要从以下几方面讲解:线程池 状态,任务的执行,线程池中线程初始化,任务缓存队列及排队策略,任务拒绝策略,线程池关闭,线程池容量的动态调整。

⑴线程池状态

AtomicInteger变量ctl的功能非常强大:利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));    private static final int COUNT_BITS = Integer.SIZE - 3;    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;    // runState is stored in the high-order bits    private static final int RUNNING    = -1 << COUNT_BITS;    //高三位111,线程池会接受新任务,并处理阻塞队列中的任务    private static final int SHUTDOWN   =  0 << COUNT_BITS;    //高三位000,不会接受新任务,但会处理阻塞队列中的任务    private static final int STOP       =  1 << COUNT_BITS;    //高三位001,不会接受新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务    private static final int TIDYING    =  2 << COUNT_BITS;    //高三位010    private static final int TERMINATED =  3 << COUNT_BITS;    //高三位011

execute()方法

public void execute(Runnable command) {        if (command == null)            throw new NullPointerException();                   //如果提交了空任务,抛出异常          int c = ctl.get();                                                                   if (workerCountOf(c) < corePoolSize) {                //workerCountOf(c)方法可以获取当前正在运行的线程数            if (addWorker(command, true))                  return;                                         //如果当前线程数量少于 corePoolSize,调用 addWorker(command, true)去增加新的核心线程。                             c = ctl.get();          }          //          if (isRunning(c) && workQueue.offer(command)) {            int recheck = ctl.get();            if (! isRunning(recheck) && remove(command))                    reject(command);                               //如果线程池被关闭,从workQueue移除command            else if (workerCountOf(recheck) == 0)                   addWorker(null, false);                        //线程池没有被关闭,并且没有 worker 在工作,则调用 addWorker(null, false) 新增线程          }           else if (!addWorker(command, false))                //插入队列不成功,且当前线程数量小于最大喜爱那城池数量,创建新线程执行任务,失败抛出异常                reject(command);    }
⑶addWorker()方法

private boolean addWorker(Runnable firstTask, boolean core) {        retry:        for (;;) {             int c = ctl.get();               //当前运行线程的数量              int rs = runStateOf(c);          //获取当前线程池状态             // Check if queue empty only if necessary.            if (rs >= SHUTDOWN &&                    ! (rs == SHUTDOWN &&                   firstTask == null &&                   ! workQueue.isEmpty()))                return false;                //如果状态显示线程池已经被关闭,或者正在被关闭,workQueue 已经空了,则直接返回false(创建新的线程失败)             for (;;) {                int wc = workerCountOf(c);                   if (wc >= CAPACITY ||                    wc >= (core ? corePoolSize : maximumPoolSize))                    return false;            //如果当前是添加核心线程,线程池数量大于等于 corePoolSize,返回false。 如果当前是添加非核心线程,线程池数量大于等于 maximumPoolSize,则直接返回false                 if (compareAndIncrementWorkerCount(c))                    break retry;             //将线程数量加1,如果增加成功,则退出循环,执行后面的代码。失败一般都是因为线程数不同步。                  c = ctl.get();  // Re-read ctl                if (runStateOf(c) != rs)     //再检查一遍运行状态,如果运行状态改变了,将从外循环开头开始执行,再次确认能否添加任务。                     continue retry;                // else CAS failed due to workerCount change; retry inner loop            }        }    //下面的代码功能是创建新的Worker实例           boolean workerStarted = false;        boolean workerAdded = false;        Worker w = null;        try {            final ReentrantLock mainLock = this.mainLock;            w = new Worker(firstTask);            final Thread t = w.thread;            if (t != null) {                mainLock.lock();                try {                    int c = ctl.get();                    int rs = runStateOf(c);                    if (rs < SHUTDOWN ||                        (rs == SHUTDOWN && firstTask == null)) {                        if (t.isAlive()) // precheck that t is startable                            throw new IllegalThreadStateException();                        workers.add(w);                        int s = workers.size();                        if (s > largestPoolSize)                            largestPoolSize = s;                        workerAdded = true;                    }                } finally {                    mainLock.unlock();                }                if (workerAdded) {         //启动新线程                     t.start();                    workerStarted = true;                }            }        } finally {            if (! workerStarted)           //如果启动线程失败                  addWorkerFailed(w);        }        return workerStarted;    }
⑷shutdown()方法

public void shutdown() {        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();                        //获取 mainLock 锁         try {            checkShutdownAccess();              //判断调用者是否有权限shutdown线程池            advanceRunState(SHUTDOWN);          //CAS+循环设置线程池状态为shutdown            interruptIdleWorkers();             //中断所有空闲线程            onShutdown();         } finally {            mainLock.unlock();                  //解锁        }        tryTerminate();                         //尝试终止线程池    }
⑸shutdownNow()方法

    public List<Runnable> shutdownNow() {        List<Runnable> tasks;        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            checkShutdownAccess();                          advanceRunState(STOP);                           interruptWorkers();                 //中断所有线程,包括正在运行任务的            tasks = drainQueue();               //将workQueue中的元素放入一个List并返回        } finally {            mainLock.unlock();        }        tryTerminate();        return tasks;    }

⑹tryTerminate()方法

    final void tryTerminate() {        for (;;) {            int c = ctl.get();            if (isRunning(c) ||                runStateAtLeast(c, TIDYING) ||                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))                return;            if (workerCountOf(c) != 0) { // Eligible to terminate                interruptIdleWorkers(ONLY_ONE);                return;            }            final ReentrantLock mainLock = this.mainLock;            mainLock.lock();            try {            // //CAS:将线程池的ctl变成TIDYING(所有的任务被终止,workCount为0,为此状态时将会调用terminated()方法),期间ctl有变化就会失败,会再次for循环                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {                    try {                        terminated();                   //需子类实现                    } finally {                        ctl.set(ctlOf(TERMINATED, 0));  //将线程池的ctl变成TERMINATED                        termination.signalAll();        //唤醒调用了 等待线程池终止的线程 awaitTermination()                     }                    return;                }            } finally {                mainLock.unlock();            }            // else retry on failed CAS        }    }

6.常见的四种线程池

Java通过Executors提供了四种线程池,这四种线程池都是直接或间接配置ThreadPoolExecutor的参数实现的

⑴CachedThreadPool()

可缓存线程池:

线程数无限制

有空闲线程则复用空闲线程,若无空闲线程则新建线程

一定程序减少频繁创建/销毁线程,减少系统开销

创建方法:

public static ExecutorService newCachedThreadPool() {    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                  60L, TimeUnit.SECONDS,                                  new SynchronousQueue<Runnable>());}

⑵FixedThreadPool()

定长线程池:

可控制线程最大并发数(同时执行的线程数)

超出的线程会在队列中等待

//nThreads => 最大线程数即maximumPoolSizeExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);//threadFactory => 创建线程的方法,这就是我叫你别理他的那个星期六!你还看!ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);源码:public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                                  0L, TimeUnit.MILLISECONDS,                                  new LinkedBlockingQueue<Runnable>());}

⑶ScheduledThreadPool()

定时线程池:

支持定时及周期性任务执行。

创建方法:

//nThreads => 最大线程数即maximumPoolSizeExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);源码:public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {    return new ScheduledThreadPoolExecutor(corePoolSize);}//ScheduledThreadPoolExecutor():public ScheduledThreadPoolExecutor(int corePoolSize) {    super(corePoolSize, Integer.MAX_VALUE,          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,          new DelayedWorkQueue());}

⑷SingleThreadExecutor()

单线程化的线程池:

有且仅有一个工作线程执行任务

所有任务按照指定顺序执行,即遵循队列的入队出队规则

创建方法:

单线程化的线程池:有且仅有一个工作线程执行任务所有任务按照指定顺序执行,即遵循队列的入队出队规则创建方法:ExecutorService singleThreadPool = Executors.newSingleThreadPool();源码:public static ExecutorService newSingleThreadExecutor() {    return new FinalizableDelegatedExecutorService        (new ThreadPoolExecutor(1, 1,                                0L, TimeUnit.MILLISECONDS,                                new LinkedBlockingQueue<Runnable>()));}

还有一个Executors.newSingleThreadScheduledExecutor()结合了3和4,就不介绍了,基本不用。