传统线程的弊端
1.重复创建线程对象,性能差;
2.线程缺乏统一管理,可能会无限制创建新的线程,线程之间存在CPU资源竞争,导致CPU占用过高,
或者发生OOM;
3.缺乏功能,例如定时。
线程池的好处
1.重用已存在的线程,减少对象的创建、销毁,性能佳
2.有效控制最大并发线程,提高资源使用率,避免过多资源竞争,避免堵塞;
3.提供定时执行、定期执行、单线程、并发控制等功能;
线程池的使用
1. newCacheThreadPoll (缓存线程池)
可以灵活回收无任务的线程,如果没有线程回收,则创建新的线程,实例如下
ExecutorService cacheThreadPoll = Executors.newCachedThreadPool(); //创建一个缓存线程池
for (int i = 0; i < 20; i++) {
final int index = i;
//execute开启一个线程任务
cacheThreadPoll.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + " 执行任务 :" + index);
}
});
}
运行代码结果如下:
分析log: 我们发现在20次线程任务中,实际被创建的线程只有12个,说明其中有一部分线程被复用了,为了更直观明显体验线程复用,对代码稍作修改
private void cacheThread() {
ExecutorService cacheThreadPoll = Executors.newCachedThreadPool(); //创建一个缓存线程池
for (int i = 0; i < 20; i++) {
final int index = i;
//execute开启一个线程任务
cacheThreadPoll.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + " 执行任务 :" + index);
}
});
try {
Thread.sleep(2000); //每次睡眠2s,直观看出线程复用效果
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果如下:
这个时候观察log,发现20个线程任务,由统一的一个线程复用完成。
2. newFixedThreadPoll (定长线程池)
创建一个定长线程池,可控制最大线程并发数,超出的线程在队列中等待
private void fixedThread() {
ExecutorService fixedThreadPoll = Executors.newFixedThreadPool(4); //创建一个缓存线程池
for (int i = 0; i < 20; i++) {
final int index = i;
//execute开启一个线程任务
fixedThreadPoll.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + " 执行任务 :" + index);
try {
Thread.sleep(1000); //每次睡眠2s,直观看出线程复用效果
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
运行结果如下:
发现线程每次并发4个任务
3. newSingleThreadEcutor (单例程池)
只会用唯一的一个工作线程去执行任务,保证所有任务按照指定顺序去执行。
ExecutorService singleThreadPoll = Executors.newSingleThreadExecutor(); //创建一个缓存线程池
for (int i = 0; i < 20; i++) {
final int index = i;
//execute开启一个线程任务
singleThreadPoll.execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + " 执行任务 :" + index);
}
});
}
4. newScheduledThreadPool (定长线程池,支持定时,周期)
//创建一个缓存线程池,单一的,可以创建多个
//第一种用法,延迟3秒执行
ScheduledExecutorService schThreadPoll = Executors.newSingleThreadScheduledExecutor();
Log.d(TAG, "开始执行3s任务 :");
//定时3s后执行
schThreadPoll.schedule(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + " 执行3s任务 :");
}
}, 3, TimeUnit.SECONDS);
//第二种,周期性任务。4s后执行,每隔3s执行一次
schThreadPoll.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + " 执行3s任务 :");
}
},4, 3, TimeUnit.SECONDS);
schThreadPoll.shutdown(); // 停止周期任务