Android APK开发基础——性能优化之多线程操作
Thread
继承Thread方式创建线程
/**
* 继承Thread方式
*/
private class Thread1 extends Thread {
Thread1(String name) {
super(name);
}
@Override
public void run() {
//执行耗时操作
while (isRunning) {
count();
try{
sleep(500);}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
线程同步
四种同步方式
1:使用特殊域变量(volatile)实现线程同步
2:同步函数
3:同步代码块
4:使用重入锁实现线程同步
/** 1:使用特殊域变量(volatile)实现线程同步 *
*
* a.volatile关键字为域变量的访问提供了一种免锁机制,
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
*/
private volatile int count = 1000;
private void count() {
if (count > 0) {
Log.e(TAG, Thread.currentThread().getName() + "--->" + count--);
} else {
isRunning = false;
}
}
/**2:同步函数*/
private synchronized void count1() {
if (count > 0) {
Log.e(TAG, Thread.currentThread().getName() + "--->" + count--);
} else {
isRunning = false;
}
}
/** 3:同步代码块*/
private void count2() {
synchronized (this) {
if (count > 0) {
Log.e(TAG, Thread.currentThread().getName() + "--->" + count--);
} else {
isRunning = false;
}
}
}
/** 4:使用重入锁实现线程同步
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁 */
ReentrantLock lock = new ReentrantLock();
private void count4() {
lock.lock();
if (count > 0) {
Log.e(TAG, Thread.currentThread().getName() + "--->" + count--);
} else {
isRunning = false;
}
lock.unlock();
}
ThreadPoolExecutor
引用,感谢作者
线程池,这一篇或许就够了https://www.jianshu.com/p/210eab345423
线程池的使用及ThreadPoolExecutor的分析(一) https://www.cnblogs.com/qm-article/p/7821602.html
为什么用线程池
-
创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率
例如:
记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3
如果T1+T3>T2,那么是不是说开启一个线程来执行这个任务太不划算了!
正好,线程池缓存线程,可用已有的闲置线程来执行新任务,避免了T1+T3带来的系统开销
-
线程并发数量过多,抢占系统资源从而导致阻塞
我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况
运用线程池能有效的控制线程最大并发数,避免以上的问题
-
对线程进行一些简单的管理
比如:延时执行、定时循环执行的策略等
运用线程池都能进行很好的实现
线程池ThreadPoolExecutor
ThreadPoolExecutor的策略
上面介绍参数的时候其实已经说到了ThreadPoolExecutor执行的策略,这里给总结一下,当一个任务被添加进线程池时:
- 线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务
- 线程数量达到了corePools,则将任务移入队列等待
- 队列已满,新建线程(非核心线程)执行任务
- 队列已满,总线程数又达到了maximumPoolSize,就会由上面那位星期天(RejectedExecutionHandler)抛出异常
常见四种线程池
如果你不想自己写一个线程池,那么你可以从下面看看有没有符合你要求的(一般都够用了),如果有,那么很好你直接用就行了,如果没有,那你就老老实实自己去写一个吧
Java通过Executors提供了四种线程池,这四种线程池都是直接或间接配置ThreadPoolExecutor的参数实现的,下面我都会贴出这四种线程池构造函数的源码,各位大佬们一看便知!
来,走起:
SingleThreadExecutor()
单线程化的线程池:
- 有且仅有一个工作线程执行任务
- 所有任务按照指定顺序执行,即遵循队列的入队出队规则
创建方法:
ExecutorService singleThreadPool = Executors.newSingleThreadPool();
源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
还有一个Executors.newSingleThreadScheduledExecutor()
结合了3和4,就不介绍了,基本不用。
FixedThreadPool()
定长线程池:
- 可控制线程最大并发数(同时执行的线程数)
- 超出的线程会在队列中等待
创建方法:
//nThreads => 最大线程数即maximumPoolSize
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);
//threadFactory => 创建线程的方法,这就是我叫你别理他的那个星期六!你还看!
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2个参数的构造方法源码,不用我贴你也知道他把星期六放在了哪个位置!所以我就不贴了,省下篇幅给我扯皮
ScheduledThreadPool()
定长线程池:
- 支持定时及周期性任务执行。
创建方法:
//nThreads => 最大线程数即maximumPoolSize
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//ScheduledThreadPoolExecutor():
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
CachedThreadPool()
可缓存线程池:
- 线程数无限制
- 有空闲线程则复用空闲线程,若无空闲线程则新建线程
- 一定程序减少频繁创建/销毁线程,减少系统开销
创建方法:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
通过我上面行云流水谈笑风生天马行空滔滔不绝的对各种参数的说明,这个源码你肯定一眼就看懂了,想都不用想(下面三种一样啦)
源码:
https://github.com/yuanhhyuan/ThreadAndThreadPoolExecutor