前言:
主线程和子线程
主线程(也叫UI线程):在java中默认情况下一个进程只有一个线程,这个线程就是主线程。其主要作用是处理和界面交互相关的逻辑,主线程不能做太多耗时的操作,否则会产生界面卡顿的感觉。为保持较快的响应速度,子线程就出场了。
子线程:也叫工作线程,除了主线程之外的都是子线程。
基本用途:主线程是运行四大组件及处理它们和用户的交互,子线程处理耗时的任务,如网络请求、I/O操作等。
gitHub 地址:https://github.com/lyyRunning/ThreadDemo
Android中的线程表现形式:
Thread、AsyncTask、HanderThread、IntentService.
简单介绍一下后三者的注意事项:
- AysncTask: 可理解为轻量级的线程池,异步任务类,其封装了Thread和Handler,通过AysncTask可以很方便的执行后台任务及在主线程中访问UI.
缺点:不适合进行特别耗时的后台任务,对于特别耗时的任务最好 用池线程池替换。
HandlerThread: 继承自Thread,它是一种可以 使用Handler和Thread.实现也很简单,在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。
IntentService: 继承自Service,是一个抽象类,所以必须创建他的子类才能使用IntentSerivce,可用于执行后台耗时任务,当任务完成后会自动停止,该优先级高于其他单纯的线程,不容易补系统杀死。 IntentSerivce 封装了Handler和HandlerThread.
线程池的好处:
- 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能消耗。
- 能有效控制线程池的最大并发数,避免大量的线程之间因相互抢占系统资源而导致的阻塞现象。
- 能对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
Android中的线程池来源于java,主要是通过Executor 来派生特定类型的线程池,不同类型的线程池有不同的意义。Executor是一个接口,真正地实现为ThreadPoolExecutor,他提供了一系列的参数来配置线程池。不同参数意味着不同的线程池。
一、ThreadPoolExecutor
ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池。下面介绍ThreadPoolExecutor的构造方法中各个参数的含义,这些参数将会直接影响线程池的功能特性,下面是ThreadPoolExecutor的一个比较常用的构造方法。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingDeque<Runnable> workQueue,
ThreadFactory threadFactory)
corePoolSize
线程池的核心数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态。如果将ThreadPoolExecutor的 allowCoreThreadTimeOut 属性设置为 true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由 KeepAliveTime 所指定,当等待时间超过KeepAliveTime所指定的时长后,核心线程就会终止。maximumPoolSize
线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。keepAliveTime
非核心线程闲置时超时时长,超过这个时长,非核心线程就会被回收。当 ThreadPool-Executor 的allowCoreThreadTimeOut属性设置为 true 时,keepAliveTime同样作用于核心线程。unit
用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有 TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS (秒)以及TimeUnit.MINUTES(分钟)等。workQueue
线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会存储在这个参数中。threadFactory
线程工厂,为线程池提供创建新线程的功能。threadFactory是一个接口,它只有一个方法:Thread new Thread(Runnable r)。
注意:
除了以上这些参数外,ThreadPoolExecutor 还有一个不常用的参数Rejected-ExecutionHandler handler。当线程池无法执行新任务时,这可能是由于任务队列已满或者是无法成功执行任务,这个时候ThreadPoolExecutor会调用 handler 的rejectedExecution方法来通知调用者,默认情况下rejectedExecution会直接抛出一个Rejected-Execution-Exception。ThreadPoolExecutor为RejectedExecutionHandler提供了几个可选的值:CallerRunsPolicy、AbortPolicy、DiscardPolicy 和DiscardOldestPolicy,其中AbortPolicy是默认值,它会直接抛出RejectedExecutionException。
- ThreadPoolExecutor执行任务时遵循如下原则:
- 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
- 如果线程池中的数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
- 如果在步骤 2 中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行。
4.如果步骤 3 中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。
- ThreadPoolExecutor的参数配置在 AsyncTask 中有明显的体现,下面试AsyncTask中的线程池的配置情况:
private static final String LOG_TAG = "AsyncTask";
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
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;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
- 从以上代码可知:AsyncTask 对ThreadPoolExecutor这个线程池的配置,规则如下:
- 核心线程数等于 CPU 核心数+1;
- 线程池的最大线程数为 CPU 核心数的 2 倍+1;
- 核心线程无超时机制,非核心线程在闲置时的超时时间为 1 秒;
- 任务队列的容量为 128;
二、线程池的分类:
Android 中常见的四类具有不同功能特性的线程池,它们都直接或间接地通过配置 ThreadPoolExecutor 来实现自己的功能特性,这四类线程池分别是 FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor。
1. FixedThreadPool()
通过 Executor 的 newFixedThreadPool方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能更加快速地响应外界的请求。newFixedThreadPool方法的实现如下,可以发现FixedThreadPool中只有核心线程并且这些核心线程没有超时机制,另外任务队列也是没有大小限制的。
线程池特点:
①可控制线程最大并发数(线程数固定)
②超出的线程会在队列中等待
使用优势:能够更快的响应外界的请求。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
2. CachedThreadPool()
通过 Executor 的 newCachedThreadPool方法来创建。它是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数为 Integer.MAX_VALUE。由于Integer.MAX_VALUE是一个很大的数,实际上就相当于最大线程数可以任意大。当线程池中的线程都处于活动状态时,线程池会创建新的线程池来处理新任务,否则就会利用空闲的线程来处理新任务。线程池中的空闲线程都有超时机制,这个超时时长是 60 秒,超过 60 秒闲置的线程就会被回收掉。
- 它适合大量的耗时较少的任务;
- 当它整个线程池都处于闲置状态,线程池的线程都会超时被停掉,CachedThreadPool
中没有任何线程,它几乎不占用任何系统资源。
缓存线程池的特点:
① 线程数无限制
② 没有核心线程,都是非核心线程
优势:比较适合用来执行大量的但是耗时较少的任务。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3. ScheduledThreadPool()
通过 Executor 的 newScheduledThreadPool方法来创建。它的核心线程数量是固定的,而非核心线程的数量是没有限制的,并且当非核心线程闲置时会被立即回收。ScheduledThreadPool这类线程池主要用于执行定时任务和具有固定周期的重复任务。
定时线程池特点:
核心线程数量固定、非核心线程数量无限制(闲置时马上回收)
使用场景:
用于执行定时任务和具有固定周期的重复任务。
// 1. 创建 定时线程池对象 & 设置线程池线程数量固定为4
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("开始执行任务");
}
};
// 3. 向线程池提交任务:schedule()
scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务
// 4. 关闭线程池
scheduledThreadPool.shutdown();
4. SingleThreadPool()
通过 Executor 的 new SingleThreadExecutor方法来创建。这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。
单线程化线程池特点:
只有一个核心线程(保证所有任务按照指定顺序在一个线程中执行,不需要处理线程同步的问题)
使用场景:
按顺序执行,不需要处理同步的问题。不适合并发但可能引起IO阻塞性及影响UI线程响应的操作,如数据库操作,文件操作等
// 1. 创建单线程化线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("开始执行任务");
}
};
// 3. 向线程池提交任务:execute()
singleThreadExecutor.execute(task);
// 4. 关闭线程池
singleThreadExecutor.shutdown();
三、线程池的使用:
1.线程池的封装
public class ExecutorServiceCreator {
/**
* 私有构造
*/
private ExecutorServiceCreator() {
}
/**
* 静态内部类
*/
private static class FixedHolder{
private static final ExecutorService fixedExecutorService = newFixedThreadPool(8);
}
/**
* FixedThreadPool线程池
* @return
*/
public static ExecutorService getFixedInstance(){
return FixedHolder.fixedExecutorService;
}
/**
* 静态内部类
*/
private static class CachedHolder{
private static final ExecutorService cachedExecutorService = newCachedThreadPool();
}
/**
* CachedThreadPool线程池
* @return
*/
public static ExecutorService getCachedInstance(){
return CachedHolder.cachedExecutorService;
}
/**
* 静态内部类
*/
private static class ScheduledHolder{
private static final ExecutorService scheduledExecutorService = newCachedThreadPool();
}
/**
* CachedThreadPool线程池
* @return
*/
public static ExecutorService getScheduledInstance(){
return ScheduledHolder.scheduledExecutorService;
}
/**
* 静态内部类
*/
private static class SingletonHolder {
private static final ExecutorService singletonExecutorService = (ExecutorService) newCachedThreadPool();
}
/**
* CachedThreadPool线程池
* @return
*/
public static ExecutorService getSingleInstance() {
return SingletonHolder.singletonExecutorService;
}
}
2.线程池的调用:
public class SecondActivity extends AppCompatActivity {
@BindView(R.id.bt_fixed)
Button btFixed;
@BindView(R.id.bt_cached)
Button btCached;
@BindView(R.id.bt_scheduled)
Button btScheduled;
@BindView(R.id.bt_single)
Button btSingle;
@BindView(R.id.bt_next)
Button btNext;
/**
* 声明自动生成
* 类似 findViewById 的效果
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
btNext.setVisibility(View.GONE);
}
/**
* 下面整个方法自动生成
*
* @param view
*/
@OnClick({R.id.bt_fixed, R.id.bt_cached, R.id.bt_scheduled, R.id.bt_single,R.id.bt_next})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.bt_fixed:
fixedInit();
break;
case R.id.bt_cached:
cachedInit();
break;
case R.id.bt_scheduled:
scheduledInit();
break;
case R.id.bt_single:
singleInit();
break;
default:
}
}
private void scheduledInit() {
ExecutorServiceCreator.getScheduledInstance().execute(new Runnable() {
@Override
public void run() {
Log.d("LUO", "scheduledInit======333");
}
});
}
private void cachedInit() {
ExecutorServiceCreator.getCachedInstance().execute(new Runnable() {
@Override
public void run() {
Log.d("LUO", "cachedInit======222");
}
});
}
private void singleInit() {
ExecutorServiceCreator.getSingleInstance().execute(new Runnable() {
@Override
public void run() {
Log.d("LUO", "singleInit======444");
}
});
}
private void fixedInit() {
ExecutorServiceCreator.getFixedInstance().execute(new Runnable() {
@Override
public void run() {
Log.d("LUO", "fixedInit======111");
}
});
}
public static void launch(Context mContext) {
Intent intent = new Intent(mContext,SecondActivity.class);
mContext.startActivity(intent);
}
}