当前位置:编程学习 > JAVA >>

JAVA 线程池

、ThreadPoolExecutor的使用

1.ThreadPoolExecutor的完整构造方法是:


ThreadPoolExecutor ( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue< Runnable >  workQueue, RejectedExecutionHandler hander)


2.Executors中的几个工厂方法

ThreadPoolExecutor是Executors类的底层实现

①newFixedThreadPool ( int nThreads)——固定大小线程池


[java]
public static ExecutorService newFixedThreadPool(int nThreads) { 
        return new ThreadPoolExecutor(nThreads, nThreads, 
                                      0L, TimeUnit.MILLISECONDS, 
                                      new LinkedBlockingQueue<Runnable>()); 
    } 

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
可以看到corePoolSize和maximunPoolSize的大小是一样的(其实使用无界Queue的话maximumPoolSize参数是没有意义的),keepAliveTime和unit的设置表明该实现不想keepAlive,最后的BlockingQueue选择了LinkedBlockingQueue,该Queue有一个特点就是它是无界的;

②newSingleThreadExecutor()——单线程


[java]
public static ExecutorService newSingleThreadExecutor() { 
        return new ThreadPoolExecutor(1, 1, 
                                      0L, TimeUnit.MILLISECONDS, 
                                      new LinkedBlockingQueue<Runnable>()); 
    } 

public static ExecutorService newSingleThreadExecutor() {
        return new ThreadPoolExecutor(1, 1,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
③newCachedThreadPool()——无界线程池,可以进行自动线程回收


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

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

在BlockingQueue的选择上使用SynchronousQueue,在该Queue中,每个插入操作必须等待另一个线程的对应移除操作。比如,我先添加一个元素,接下来如果想继续添加则会阻塞,直到另一个线程取走一个元素,反之亦然(也就是缓冲区为1的生产者消费者模式)。

3.corePoolSize、workQueue、maximumPoolSize的优先级

当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。

如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。

如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。


也就是:处理任务的优先级为:

核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。


二、BlockingQueue<Runnable> workQueue

1.BlockingQueue的作用

所有的BlockingQueue都可以用于传输和保持提交的任务,可以使用此队列对池大小进行交互。

如果运行的线程小于corePoolSize,则Executor始终首选添加新的线程,而不进行排队,也就是当当前运行的线程小于corePoolSize时,则任务根本不会存放,添加到Queue中,而是直接创建一个新的thread;

如果运行的线程等于或多余corePoolSize,则Executor首选将请求加入Queue,而不是添加新的thread;

如果无法将请求加入Queue,则创建新线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。

2.排队的三种策略

①直接提交,工作队列的默认选项是SynchronousQueue,它将任务交给线程而不保持它们,如果不存在可用于立即执行的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界maximumPoolSize以避免拒绝新提交的任务,当命令以超过队列所能处理的平均连续到达时,此策略允许无界线程具有增长的可能性。

②无界队列,使用无界队列如LinkedBlockingQueue将导致在所有corePoolSize线程都忙时新任务在队列中等待,这样创建的线程数就不会超过corePoolSize,因此maximumPoolSize的值也就无效了。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列。

③有界队列,当使用有限的maximumPoolSize时,有界队列有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的

补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,