1. 线程池使用
使用new Thread()
创建线程存在的问题:
- 频繁创建线程,执行完后又被回收,导致频繁GC
- 多线程缺乏统一管理,各线程之间互相竞争,降低程序运行效率
- 无法有效控制线程的执行、取消等。
使用线程池的优点:
- 重用线程池中的线程,避免线程的创建和销毁带来的性能开销
- 有效控制线程池的最大并发数,避免大量线程之间因胡抢占资源导致阻塞现象
- 对线程进行简单管理,并提供定时执行,指定间隔循环执行等
Android中的线程池源于Java中的Executor
,其本身是一个接口,真正的实现类是ThreadPoolExecutor
。
1.1 ThreadPoolExecutor
ThreadPoolExecutor
有四个重载的构造方法,介绍以下参数最多的构造器如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数含义:
-
corePoolSize: 线程池中核心线程的数量,默认情况会在线程池中一直存活。可以通过
allowCoreThreadTimeOut
和keepAliveTime
设置核心线程闲置超时终止。 - maximumPoolSize: 线程池中最大线程数量,活动线程数达到这个数,后续新任务会阻塞
- keepAliveTime: 非核心线程的超时时长
- unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等(TimeUnit.MILLISECONDS\TimeUnit.SECONDS........)
- workQueue 线程池中的任务队列, 通过线程池execute方法提交的Runnable对象会存储在这个参数中
-
threadFactory 为线程池提供创建新线程的功能, 是一个接口,只有一个方法:
Thread newThead(Runnable r)
-
handler 拒绝策略 当线程无法执行新任务时,默认情况下,当线程池无法处理新线程时,会抛出一个
RejectedExecutionException
(拒绝策略有:CallerRunsPolicy
、AbortPolicy
、DiscardPolicy
、DiscardOldestPolicy
)
运行规则:
- 运行线程,线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行
- 运行线程,核心线程数已满且workQueue未满,新线程放入workQueue中等待执行
- 运行线程,核心线程已满、workQueue已满且未超过非核心线程数,开启一个非核心线程来执行任务
- 运行线程,三者都满,拒绝执行该任务
示例:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
poolExecutor = new ThreadPoolExecutor(3, 5,
1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(128));
}
public void btnClick(View view) {
for (int i = 0; i < 30; i++) {
final int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
Log.d("google_lenve_fb", "run: " + finalI);
}
};
poolExecutor.execute(runnable);
}
}
线程池中线程相关参数该怎么配置呢?可以参考AsyncTask。其源码如下:
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "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);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
....
....
}
从上述代码我们可以知道:
-
核心线程数为手机CPU数 + 1(cpu数量获取方式
Runtime.getRuntime().availableProcessors()
) - 线程池线程数为手机CPU数*2 + 1
- 线程队列大小:128
如果要使用线程池,可以参考这个再根据实际配置。
1.2 线程池分类
线程池可以分为四类:FixedThreadPool
、CachedThreadPool
、ScheduledThreadPool
、以及SingleThreadExecutor
-
FixedThreadPool
是一个核心线程数量固定的线程池。通过Executors的
newFixedThreadPool
创建。ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
其源码为:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
FixedThreadPool中没有非核心线程,所有的线程都是核心线程,它们不会被回收,除非线程池被关闭了。核心线程没有超时机制。
-
CachedThreadPool
通过Executor的
newCachedThreadPool
,是一种线程数量不定的线程池,没有核心线程,最大线程数Integer.MAX_VALUE
,有线程超时机制,超时时间60秒。使用SynchronousQueue
作为线程队列,适合执行大量的耗时较少任务。源码如下:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
-
ScheduledThreadPool
通过Executor的
newScheduledThreadPool
创建,核心线程数固定,非核心线程数没有限制。一旦非核心线程闲置被立即回收。适合执行定时任务和具有固定周期的重复任务。public static ScheduledExecutorService new ScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
-
SingleThreadPool
通过Executor的
newSingleThreadPool
创建。只有一个核心线程public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
1.3 线程池常用功能
1.shutDown()
关闭线程池,不影响已经提交的任务
2.shutDownNow()
关闭线程池,并尝试去终止正在执行的线程
3.allowCoreThreadTimeOut(boolean value)
允许核心线程闲置超时时被回收
4.submit
一般情况下我们使用execute来提交任务,但是有时候可能也会用到submit,使用submit的好处是submit有返回值
网友评论