深入线程池
Posted this.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入线程池相关的知识,希望对你有一定的参考价值。
之前在读Java进阶书籍的时候,接触到了关于线程池的使用,但是一直没有进行系统的理解。这篇博客主要是对线程池功能的梳理。
使用线程池来执行任务相对于线程来讲有许多优点:
1.能够重用线程池里的线程,减少创建线程的开销。
2.可以控制线程池中的最大并发数。
3.可以对线程进行简单的管理。
线程池的简单使用
public class Test
public static void main(String[] args)
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.execute(new MyThread());
executor.execute(new MyThread());
executor.execute(new MyThread());
executor.shutdown();
static class MyThread extends Thread
@Override
public void run()
System.out.println(Thread.currentThread().getName() + ":running");
运行后,每个线程就会执行起来:
pool-1-thread-1run
pool-1-thread-3run
pool-1-thread-2run
上述的示例是通过Executors.newFixedThreadPool这个方法来生成一个线程池,当然,在编码的时候会发现Executors不仅仅只有newFixedThreadPool这个方法,还有其他的方法来生成线程池,那它们之前有什么区别?
在了解其中的区别之前,需要先了解一个线程池真正是在哪里创建的,查看newFixedThreadPool的源码:
public static ExecutorService newFixedThreadPool(int nThreads)
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
上述是通过创建了ThreadPoolExecutor对象来创建线程池。其中,该类的构造方法所需的参数需要了解一下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
corePoolSize:线程池中核心线程的数量,核心线程会一直存活。但是可以把该类中的allowsCoreThreadTimeOut方法的参数传入true以及设定keepAliveTime的时间来终止线程。意味着当核心线程的空闲时间超出keepAliveTime时也会被终止。如果线程池中核心线程未到达指定的数量,新加的任务就会直接启动一条核心线程来执行。
maximumPoolSize:线程池中线程的最大数量。等于核心线程+非核心线程。
keepAliveTime:线程空闲的最大时间,超出时间,非核心线程就会被终止。
unit:指定keepAliveTime的时间单位。
workQueue:工作队列,通过execute方法可以往队列添加线程任务。
threadFactory:用于创建新线程。
有了上述知识的基础后,再继续分析各种线程池的区别:
方法:newFixedThreadPool
该方式会产生一个线程数量固定的线程池,方法的参数即为线程数量。池内的线程处于空闲状态时,不会被回收。当线程池内线程数量达到设定的参数时,新来的任务就会被阻塞,直到有线程执行完毕。
```
public static ExecutorService newFixedThreadPool(int nThreads)
//核心线程数量和最大线程数量,说明该线程池中所有的线程都是核心线程。另外,超时时间为0,即没有超时机制。
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
方法:newCachedThreadPool
public static ExecutorService newCachedThreadPool()
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
由上面的参数可以知道,该线程池中没有核心线程,而且该线程池中线程的数量不固定,最大线程数也相当大。当线程池中有空闲的线程时,就会被用来执行新任务,否则,超时60s后就会被终止。这类线程池用于执行大量且耗时小的任务。
方法:newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor()
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
可以看到该线程池中只有一条线程,意味着每次只能执行一条线程,并且需要等该线程执行完毕后才能继续执行下一条线程。这种情况下就可以不用考虑并发导致数据冲突错误的发生。
方法:newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
return new ScheduledThreadPoolExecutor(corePoolSize);
public ScheduledThreadPoolExecutor(int corePoolSize)
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
该线程池核心线程数量固定,非核心线程数量没有限制。当非核心线程空闲时会立即被回收。该线程池可以通过调用schedule方法来设定执行线程的延时。可用于执行周期性,定时的任务。
关于AsyncTask中的线程池
AsyncTask是一个轻量级的异步执行任务的框架。里面也封装了线程池用于处理多个线程的异步任务。那AsyncTask里的线程池是如何被定义的?
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory()
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r)
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以在最后一个方法中明显地看到该线程池的配置:
CORE_POOL_SIZE:CPU核心数+1,核心线程数量。
MAXIMUM_POOL_SIZE:最大线程数量为CPU核心数的2倍+1
KEEP_ALIVE :线程池核心线程无超时机制,非核心超时时间为1秒
LinkedBlockingQueue(128):任务队列容量为128
以上是关于深入线程池的主要内容,如果未能解决你的问题,请参考以下文章
Java Review - 创建线程和线程池时建议指定与业务相关的名称