Java ExecutorService四种线程池及ThreadPoolExecutor机制

1、为何使用线程池java

使用new Thread执行多个线程有以下一些问题:数据库

每次new Thread新建对象性能差。
线程缺少统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源致使死机或oom。
缺少更多功能,如定时执行、按期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:缓存

重用存在的线程,减小对象建立、消亡的开销,性能佳。
可有效控制最大并发线程数,提升系统资源的使用率,同时避免过多资源竞争,避免堵塞。
提供定时执行、按期执行、单线程、并发数控制等功能。

并发

2、怎么使用线程池ide

java中提供的四种线程池都是Executors提供的,共计四种。性能

1, newCachedThreadPoolthis

建立一个可缓存线程池,若是线程池长度超过处理须要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码以下:spa

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}线程

cachedThreadPool.execute(new Runnable() {code

@Override
public void run() {
System.out.println(index);
}
});
}

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

2, newFixedThreadPool

建立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码以下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {


@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}

由于线程池大小为3,每一个任务输出index后sleep 2秒,因此每两秒打印3个数字。 定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。可参考PreloadDataCache。

3, newScheduledThreadPool

建立一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码以下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {

@Override
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);

表示延迟3秒执行。

按期执行示例代码以下:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);

表示延迟1秒后每3秒执行一次。

4, newSingleThreadExecutor

建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码以下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {

@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}

结果依次输出,至关于顺序执行各个任务。 现行大多数GUI程序都是单线程的。Android中单线程可用于数据库操做,文件操做,应用批量安装,应用批量删除等不适合并发但可能IO阻塞性及影响UI线程响应的操做。

3、分析四种线程池

先看下四种线程池的构建代码:

1, newCachedThreadPool

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

2, newFixedThreadPool

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

3, newScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

4, newSingleThreadExecutor

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

从上面能够看出这四种构造方法最终都是返回了ThreadPoolExecutor对象,下面重点分析下它,看下它的构造方法:

public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime,//线程池中超过corePoolSize数目的空闲线程最大存活时间;能够allowCoreThreadTimeOut(true)成为核心线程的有效时间
TimeUnit unit,//keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue,//阻塞任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler) {//当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

重点讲解:

其中比较容易让人误解的是:corePoolSize,maximumPoolSize,workQueue之间关系。

当线程池小于corePoolSize时,新提交任务将建立一个新线程执行任务,即便此时线程池中存在空闲线程。
当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行。
当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会建立新线程执行任务。
当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理。
当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程。
当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭。
那么学会使用ThreadPoolExecutor的参数后,咱们就能够不用局限于最上面那四种线程池,能够按照须要来构建本身的线程池。

另外,经过ThreadFactory能够实现对线程的命名,具体代码以下:

executor = Executors.newCachedThreadPool(new ThreadFactory() {

final AtomicInteger threadNumber = new AtomicInteger(1);

public Thread newThread(Runnable runnable) { // Use our own naming scheme for the threads. Thread thread = new Thread(Thread.currentThread().getThreadGroup(), runnable, "pool-spark" + threadNumber.getAndIncrement(), 0); //这里实现命名 // Make workers daemon threads. thread.setDaemon(true); if (thread.getPriority() != Thread.NORM_PRIORITY) { thread.setPriority(Thread.NORM_PRIORITY); } return thread; } });

相关文章
相关标签/搜索