目录
c、ScheduledThreadPool.Scheduled:
1.多线程开发
1)多线程开发目的
当我们应用启动时候,系统会为我们创建一个主线程。主线程可以向我们UI控件分发事件以及绘制页面等事件。所以我们也常称之为UI线程。
开发的时候要避免在UI线程做一些耗时的操作,因为会阻塞UI线程,导致停止事件的分发,此时的界面就像是卡住一般。更严重的情况,如果耗时操作时间大于5S,程序还会ANR,此时系统会弹窗提示我们程序无响应。
还有2种情况,不过这2种ANR并不会弹窗提示,仅是输出log而已:
- 主线程在执行BroadcastReceiver的onReceive函数时10秒内没有执行完毕
BroadcastReceiver(简称BR)的onReceive函数运行在主线程中,当这个函数超过10秒钟没有返回就会触发ANR
- 主线程在执行Service的各个生命周期函数时20秒内没有执行完毕
Service的各个生命周期函数也运行在主线程中,当这些函数超过20秒钟没有返回就会触发ANR。
因此,将耗时操作放到非UI线程中显得尤为重要,后面说一些开发中常见的工作线程。
2)多线程开发注意点
不要在非UI线程更新UI组件,可以尝试以下方式:
a、Activity.runOnUiThread(Runnable)
b、View.post(Runnable) View.postDelayed(Runnable, long)
c、handler
2.工作线程类型
1)Thread
new Thread(new Runnable() {
public void run() {
final Bitmap bitmap = 网络下载的图片;
//工作线程切换至UI线程更新UI
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();
2)intentService
特点:intentService处理完耗时操作自动销毁自身。但是页面在网络订阅事件完成前退出,还是需要手动销毁订阅事件。
intentService:内部开启了一个Worker Thread(工作线程)处理队列中的Intent,处理完成一个之后在处理第二个,每个请求都会在单独的Worker Thread中处理
public class MyIntentService extends IntentService {
//构造方法 一定要实现此方法否则Service运行出错。
public MyIntentService() {
super("MyIntentService");
}
// onHandleIntent方法-耗时操作的处理
@Override
protected void onHandleIntent(Intent intent) {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("MyIntentService","睡眠结束");
}
}
//FIXME 多次启动新任务--发现第一个执行完才会去执行第二个任务
startService(new Intent(this,MyIntentService.class));
startService(new Intent(this,MyIntentService.class));
3)handlerThread
我的这篇文章中讲到了handlerThread的用法:https://blog.csdn.net/qq_37321098/article/details/81535449
handlerThread和intentService类似,都会处理完上个任务,再去处理下个任务。
4)AsyncTask
AsyncTask也可以看我的这篇:https://blog.csdn.net/qq_37321098/article/details/81625580
AsyncTask可以串行或者并行的去执行后台任务。内部封装了Handler,4个重要的方法,除了doInBackGround方法是在工作线程运行,其余的都是在主线程运行,省去了线程切换去更新UI的操作。
5)线程池
线程池对线程做了统一的管理,不需要我们来一个耗时任务就开一个线程,这样会造成不必要的开销。我们可以根据业务需求选择合适的线程池类型去实现。常见的几种线程池如下:
a、FixedTreadPool:
线程数量固定的线程池,仅有核心线程且没有超时策略,所以线程不会被回收。这意味着它能够快速的响应外界请求。
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(runnable);
executorService.execute(runable1);
executorService.execute(runable2);
executorService.execute(runable3);
上面代码含义为同时有2个核心线程处理任务,当其中一个任务执行完毕才会去执行下一个。
b、CachedThreadPool:
是一种线程数量不定的线程池,只有非核心线程,可以简单理解为最大线程数是无限大的。CachedThreadPool的任务队列相当于一个空集合,这导致任何任务都会被立即执行,比较适合做一些大量的耗时较少的任务。
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(runnable);
executorService.execute(runable1);
executorService.execute(runable2);
executorService.execute(runable3);
上面代码含义为同时执行4个任务,但是顺序不一定和执行的顺序一样。
c、ScheduledThreadPool.Scheduled:
核心线程池的数量书固定的且非核心线程池是没有限制的,非核心线程池被闲置时会被立即回收。
主要用于执行定时任务和具有固定周期的重复任务。
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.schedule(runnable, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable1, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable2, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable3, 2000, TimeUnit.MILLISECONDS);
上面代码含义为延时2S执行任务
scheduledExecutorService.scheduleAtFixedRate(runnable,1000,1000,TimeUnit.MILLISECONDS);
上面代码含义为延迟1S,然后每隔1S执行一次runnable的任务
d、SingleThreadExecutor:
只有一个核心线程,所有的任务都是在一个线程里顺序执行。
ExecutorService executorService=Executors.newSingleThreadExecutor();
executorService.execute(runnable);
e、4种常见线程池的配置
参考自:https://blog.csdn.net/u012702547/article/details/52259529
Android中常用的线程池都是通过对ThreadPoolExecutor进行不同配置来实现的。举个例子:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
看下ThreadPoolExecutor的这个参数最多的构造方法(有4个):
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize 线程池中核心线程的数量
maximumPoolSize 线程池中最大线程数量
keepAliveTime 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长
unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等
workQueue 线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。
threadFactory 为线程池提供创建新线程的功能,这个我们一般使用默认即可
handler 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。
f、线程池常见方法
ExecutorService.shutdown():阻止新来的任务提交,对已经提交了的任务不会产生任何影响。通过将线程池的状态改成SHUTDOWN,当再将执行execute提交任务时,如果测试到状态不为RUNNING,则抛出rejectedExecution,从而达到阻止新任务提交的目的。
ExecutorService.shutdownNow():阻止新来的任务提交,同时会中断当前正在运行的线程,即workers中的线程。另外它还将workQueue中的任务给移除,并将这些任务添加到列表中进行返回。通过将线程池的状态改成STOP,当再将执行execute提交任务时,如果测试到状态不为RUNNING,则抛出rejectedExecution,从而达到阻止新任务提交的目的.
ExecutorService.awaitTermination(long timeout, TimeUnit unit):这个方法有两个参数,一个是timeout即超时时间,另一个是unit即时间单位。这个方法会使线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。例如:
ExecutorService.isTerminated:当调用shutdown()方法后,并且所有提交的任务完成后返回为true,这个方法会校验ExecutorService当前的状态是否为“TERMINATED”即关闭状态,当为“TERMINATED”时返回true否则返回false。
ExecutorService service = Executors.newFixedThreadPool(3);
service.submit(runnbale);
service.submit(runnbale1);
service.submit(runnbale2);
service.shutdown();
System.out.println(service.isTerminated());
日志显示为false,即没有关闭,因为要等待当前任务都执行完,才会返回false。
ExecutorService.iisShutdown:当调用shutdown()方法后返回为true。判断是否为阻止新任务接收的状态SHUTDOWN。
ExecutorService.submit:与execute执行不同,submit的提交是带返回值的,会将Future返回。