thread
线程几种状态
创建(new) Thread thread=new Thread;
就绪(runnable) thread.start();
运行(running) 线程只能从就绪状态进入到运行状态
阻塞(blocked) 该状态是线程因为某种原因放弃了cpu使用权限,暂时停止运行。
使用线程的几种方法
- 继承Thread
private class MyThread extends Thread {
SyncThread(String name) {
super(name);
}
@Override
public void run() {
//执行耗时操作
}
}
- 实现Runnable接口
private class MyThread implements Runnable {
@Override
public void run() {
//执行耗时操作
}
}
- 匿名内部类创建线程
new Thread(new Runnable() {
public void run() {
//执行耗时操作
}
}).start();
匿名内部类方法,能这样设置优先级
thread-priority.pnghandler
问:为什么要用 Handler消息传递机制
答:多个线程并发更新UI的同时 保证线程安全
问:怎么实现线程切换的
答:Handler 对象在哪个线程下构建(Handler的构造函数在哪个线程下调用),那么Handler就会持有这个线程的Looper引用和这个线程的消息队列的引用。因为持有这个线程的消息队列的引用,意味着这个Handler对象可以在任意其他线程给该线程的消息队列添加消息,也意味着Handler的handlerMessage 肯定也是在该线程执行的
创建handler的时候,也是先要创建looper的,主线程默认有主线程的looper,如果handler在子线程创建的,那么需要先prepare好looper给handler设置的,要不handler无法创建。。所以,looper是线程切换的关键。。
内存泄露
当Handler消息队列 还有未处理的消息 / 正在处理消息时,存在引用关系: “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” (message持有target,即handler,handler不释放,外部类无法释放。。。)
若出现 Handler的生命周期 > 外部类的生命周期 时(即 Handler消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁时),将使得外部类无法被垃圾回收器(GC)回收,从而造成 内存泄露
- 存在“未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系
- Handler的生命周期 > 外部类的生命周期
- 即 Handler消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁
使得上述任1条件不成立 即可
解决方案1:静态内部类+弱引用
解决方案2:当外部类结束生命周期时,清空Handler内消息队列
AsyncTask
AsyncTask的实现原理 = 2个线程池(任务队列线程池,执行线程池) + Handler
其中:线程池用于线程调度、复用 & 执行任务;Handler 用于异步通信
AsyncTask类属于抽象类,即使用时需 实现子类
MyTask必须在UI线程中创建 mTask.execute()必须在UI线程中调用
public abstract class AsyncTask<Params, Progress, Result> {
...
}
// 类中参数为3种泛型类型
// 整体作用:控制AsyncTask子类执行线程任务时各个阶段的返回类型
// 具体说明:
// a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数
// b. Progress:异步任务执行过程中,返回下载进度值的类型
// c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致
// 注:
// a. 使用时并不是所有类型都被使用
// b. 若无被使用,可用java.lang.Void类型代替
// c. 若有不同业务,需额外再写1个AsyncTask的子类
}
/**
* 步骤1:创建AsyncTask子类
* 注:
* a. 继承AsyncTask类
* b. 为3个泛型参数指定类型;若不使用,可用java.lang.Void类型代替
* c. 根据需求,在AsyncTask子类内实现核心方法
*/
private class MyTask extends AsyncTask<Params, Progress, Result> {
....
// 方法1:onPreExecute()
// 作用:执行 线程任务前的操作
// 注:根据需求复写
@Override
protected void onPreExecute() {
...
}
// 方法2:doInBackground()
// 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
// 注:必须复写,从而自定义线程任务
@Override
protected String doInBackground(String... params) {
...// 自定义的线程任务
// 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
publishProgress(count);
}
// 方法3:onProgressUpdate()
// 作用:在主线程 显示线程任务执行的进度
// 注:根据需求复写
@Override
protected void onProgressUpdate(Integer... progresses) {
...
}
// 方法4:onPostExecute()
// 作用:接收线程任务执行结果、将执行结果显示到UI组件
// 注:必须复写,从而自定义UI操作
@Override
protected void onPostExecute(String result) {
...// UI操作
}
// 方法5:onCancelled()
// 作用:将异步任务设置为:取消状态
@Override
protected void onCancelled() {
...
}
}
/**
* 步骤2:创建AsyncTask子类的实例对象(即 任务实例)
* 注:AsyncTask子类的实例必须在UI线程中创建
*/
MyTask mTask = new MyTask();
/**
* 步骤3:手动调用execute(Params... params) 从而执行异步线程任务
* 注:
* a. 必须在UI线程中调用
* b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
* c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
* d. 不能手动调用上述方法
*/
mTask.execute();
注意:
- AsyncTask子类的实例必须在UI线程中创建
-
mTask.execute
- a. 必须在UI线程中调用
- b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
- c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
- d. 不能手动调用上述方法
关于 生命周期
- 结论: AsyncTask不与任何组件绑定生命周期
- 使用建议: 在Activity 或 Fragment中使用 AsyncTask时,最好在Activity 或 Fragment的onDestory()调用cancel(boolean);
关于 内存泄漏
- 结论 :若AsyncTask被声明为Activity的非静态内部类,当Activity需销毁时,会因AsyncTask保留对Activity的引用 而导致Activity无法被回收,最终引起内存泄露
- 使用建议: AsyncTask应被声明为Activity的静态内部类
线程任务执行结果 丢失
- 结论: 当Activity重新创建时(屏幕旋转 / Activity被意外销毁时后恢复),之前运行的AsyncTask(非静态的内部类)持有的之前Activity引用已无效,故复写的onPostExecute()将不生效,即无法更新UI操作
- 使用建议: 在Activity恢复时的对应方法 重启 任务线程
HandlerThread
HandlerThread的本质:继承Thread类 & 封装Handler类
通过继承Thread类,快速地创建1个带有Looper对象的新工作线程
通过封装Handler类,快速创建Handler & 与其他线程进行通信
HandlerThread 构造方法可以设置优先级,,创建HandlerThread类对象 = 创建Thread类对象 + 设置线程优先级 = 新开1个工作线程 + 设置线程优先级
注意: HandlerThread的run方法内才准备looper,所以,外部使用HandlerThread时候,一定要先start开启线程(内部原理:start后开启工作线程,线程的run方法内才创建looper,所以,没法将looper给handler绑定(getLooper方法内wait阻塞了),而是通过持有锁机制先等待looper创建好,然后通知原本getLooper那等着获取looper对象的地方,接着创建handler,接着获取,接着绑定)准备好工作线程
HandlerThread的使用步骤分为5步
// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 步骤5:结束线程,即停止线程的消息循环
mHandlerThread.quit();
IntentService
IntentService本质 = Handler + HandlerThread
步骤1:定义 IntentService的子类
传入线程名称、复写onHandleIntent()方法
public class myIntentService extends IntentService {
/**
* 在构造函数中传入线程名字
**/
public myIntentService() {
// 调用父类的构造函数
// 参数 = 工作线程的名字
super("myIntentService");
}
/**
* 复写onHandleIntent()方法
* 根据 Intent实现 耗时任务 操作
**/
@Override
protected void onHandleIntent(Intent intent) {
// 根据 Intent的不同,进行不同的事务处理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
/**
* 复写onStartCommand()方法
* 默认实现 = 将请求的Intent添加到工作队列里
**/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
步骤2:在Manifest.xml中注册服务
<service android:name=".myIntentService">
<intent-filter >
<action android:name="cn.scu.finch"/>
</intent-filter>
</service>
步骤3:在Activity中开启Service服务
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 同一服务只会开启1个工作线程
// 在onHandleIntent()函数里,依次处理传入的Intent请求
// 将请求通过Bundle对象传入到Intent,再传入到服务里
// 请求1
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
// 请求2
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次启动
}
}
源码分析
IntentService 开启线程:通过onCreate中启动HandlerThread线程,创建ServiceHandler处理通知。
@Override
public void onCreate() {
super.onCreate();
// 1. 通过实例化andlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
// HandlerThread继承自Thread,内部封装了 Looper
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 2. 获得工作线程的 Looper & 维护自己的工作队列
mServiceLooper = thread.getLooper();
// 3. 新建mServiceHandler & 绑定上述获得Looper
// 新建的Handler 属于工作线程 ->>分析1
mServiceHandler = new ServiceHandler(mServiceLooper);
}
/**
* 分析1:ServiceHandler源码分析
**/
private final class ServiceHandler extends Handler {
// 构造函数
public ServiceHandler(Looper looper) {
super(looper);
}
// IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作线程中执行
// onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
onHandleIntent((Intent)msg.obj);
// 执行完调用 stopSelf() 结束服务
stopSelf(msg.arg1);
}
}
/**
* 分析2: onHandleIntent()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
@WorkerThread
protected abstract void onHandleIntent(Intent intent);
/**
* onStartCommand()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
public int onStartCommand(Intent intent, int flags, int startId) {
// 调用onStart()->>分析1
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
/**
* 分析1:onStart(intent, startId)
**/
public void onStart(Intent intent, int startId) {
// 1. 获得ServiceHandler消息的引用
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
// 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
//这里的Intent = 启动服务时startService(Intent) 里传入的 Intent
msg.obj = intent;
// 3. 发送消息,即 添加到消息队列里
mServiceHandler.sendMessage(msg);
}
工作任务队列 = 顺序执行
即 若一个任务正在IntentService中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行
静态启动service
// 在IntentService中,onBind()`默认返回null
@Override
public IBinder onBind(Intent intent) {
return null;
}
采用 bindService()启动 IntentService的生命周期如下:
onCreate() ->> onBind() ->> onunbind()->> onDestory()
即,并不会调用onStart() 或 onStartcommand(),故不会将消息发送到消息队列,那么onHandleIntent()将不会回调,即无法实现多线程的操作,此时,你应该使用Service,而不是IntentService
从上面源码可看出:IntentService本质 = Handler + HandlerThread:
- 通过HandlerThread 单独开启1个工作线程:IntentService
- 创建1个内部 Handler :ServiceHandler
- 绑定 ServiceHandler 与 IntentService
- 通过 onStartCommand() 传递服务intent 到ServiceHandler 、依次插入Intent到工作队列中 & 逐个发送给 onHandleIntent()
- 通过onHandleIntent() 依次处理所有Intent对象所对应的任务
- 因此我们通过复写onHandleIntent() & 在里面 根据Intent的不同进行不同线程操作 即可
ThreadPool
https://blog.csdn.net/qq_41648631/article/details/102871630
线程池的好处:
重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
能对线程进行简单的管理。并提供定时执行以及指定间隔循环执行等功能。
最基本的构造方法
public ThreadPoolExecutor(
//核心线程数,除非allowCoreThreadTimeOut被设置为true,否则它闲着也不会死
//线程池维护线程的最少数量。线程池至少会保持改数量的线程存在,即使没有任务可以处理。(注意:这里说的至少是指线程达到这个数量后,即使有空闲的线程也不会释放,而不是说线程池创建好之后就会初始化这么多线程)
int corePoolSize,
//最大线程数,活动线程数量超过它,后续任务就会排队
//线程池维护线程的最大数量。线程池最多可创建的线程数,即使队列中的任务满了线程数量也不会超过maximumPoolSize
int maximumPoolSize,
//超时时长,作用于非核心线程(allowCoreThreadTimeOut被设置为true时也会同时作用于核心线程),闲置超时便被回收
long keepAliveTime,
//枚举类型,设置keepAliveTime的单位,有TimeUnit.MILLISECONDS(ms)、TimeUnit. SECONDS(s)等
TimeUnit unit,
//缓冲任务队列,线程池的execute方法会将Runnable对象存储起来
//ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue,PriorityBlockingQueue
BlockingQueue<Runnable> workQueue,
//线程工厂接口,只有一个new Thread(Runnable r)方法,可为线程池创建新线程
ThreadFactory threadFactory,
//饱和策略,线程池对拒绝任务的处理策略。AbortPolicy,CallerRunsPolicy,DiscardOldestPolicy,DiscardPolicy,自定义
RejectedExecutionHandler handler) {
线程池工作原理
execute一个线程之后,如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行。
execute一个线程之后,如果线程池中的线程数已经达到核心线程数,且workQueue未满,则将新线程放入workQueue中等待执行。
execute一个线程之后,如果线程池中的线程数已经达到核心线程数但未超过非核心线程数,且workQueue已满,则开启一个非核心线程来执行任务。
execute一个线程之后,如果线程池中的线程数已经超过非核心线程数,则拒绝执行该任务,采取饱和策略,并抛出RejectedExecutionException异常。
总的来说:
1、maximumPoolSize就是线程池最大线程对象的容量 > 核心 + 非核心
2、就任务而言,先紧着核心线程,满了后,在核心现场的等待队列排队,再满了,说明任务很多啊,来的又太快,只能继续开非核心线程
3、任务较多,要用到阻塞策略,任务太多,要用到饱和策略
阻塞
缓存队列,就是阻塞队列,就是核心线程不够用时候的一个缓存,分几种,BlockingQueue类型的
ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。FIFO,需要指定队列大小,如果队列满了,会触发线程池的RejectedExecutionHandler逻辑
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。FIFO,可以无限向队列中添加任务,直到内存溢出
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。优先级队列,线程池会优先选取优先级高的任务执行,队列中的元素必须实现Comparable接口
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。可以简单理解为是一个容量只有1的队列。Executors.newCachedThreadPool使用的是这个队列
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
饱和策略
也就是线程数超过maximumPoolSize,会抛异常
当任务队列和线程池都满了时所采取的应对策略,默认 是AbordPolicy,表示无法处理新任务,并抛出RejectedExecutionException异常。
AbortPolicy: 线程池默认的策略,如果元素添加到线程池失败,会抛出RejectedExecutionException异常
DiscardPolicy: 如果添加失败,则放弃,并且不会抛出任何异常
DiscardOldestPolicy: 如果添加到线程池失败,会将队列中最早添加的元素移除,再尝试添加,如果失败则按该策略不断重试
CallerRunsPolicy: 如果添加失败,那么主线程会自己调用执行器中的execute方法来执行改任务
自定义: 如果觉得以上几种策略都不合适,那么可以自定义符合场景的拒绝策略。需要实现RejectedExecutionHandler接口,并将自己的逻辑写在rejectedExecution方法内。
线程池分类
1、newCachedThreadPool:
底层:返回ThreadPoolExecutor实例,corePoolSize为0;maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为60L;unit为TimeUnit.SECONDS;workQueue为SynchronousQueue(同步队列)
通俗:当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。
适用:执行很多短期异步的小程序或者负载较轻的服务器
2、newFixedThreadPool:
底层:返回ThreadPoolExecutor实例,接收参数为所设定线程数量nThread,corePoolSize为nThread,maximumPoolSize为nThread;keepAliveTime为0L(不限时);unit为:TimeUnit.MILLISECONDS;WorkQueue为:new LinkedBlockingQueue() 无解阻塞队列
通俗:创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不再添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)
适用:执行长期的任务,性能好很多
3、newSingleThreadExecutor:
底层:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例,corePoolSize为1;maximumPoolSize为1;keepAliveTime为0L;unit为:TimeUnit.MILLISECONDS;workQueue为:new LinkedBlockingQueue() 无解阻塞队列
通俗:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)
适用:一个任务一个任务执行的场景
4、NewScheduledThreadPool:
底层:创建ScheduledThreadPoolExecutor实例,corePoolSize为传递来的参数,maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为0;unit为:TimeUnit.NANOSECONDS;workQueue为:new DelayedWorkQueue() 一个按超时时间升序排序的队列
通俗:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构
适用:周期性执行任务的场景
自定义阻塞,按优先级
public abstract class PriorityRunnable implements Runnable,Comparable<PriorityRunnable> {
private int priority;
public PriorityRunnable(int priority){
if(priority <0) {
throw new IllegalArgumentException();
}
this.priority = priority;
}
public int getPriority() {
return priority;
}
@Override
public int compareTo(@NonNull PriorityRunnable another) {
int me = this.priority;
int anotherPri=another.getPriority();
return me == anotherPri ? 0 : me < anotherPri ? 1 : -1;
}
@Override
public void run() {
doSomeThing();
}
protected abstract void doSomeThing();
}
自定义饱和
如果以上策略都不符合业务场景,那么可以自己定义一个拒绝策略,只要实现RejectedExecutionHandler接口,并且实现rejectedExecution方法就可以了。具体的逻辑就在rejectedExecution方法里去定义就OK了。
例如:我定义了我的一个拒绝策略,叫做MyRejectPolicy,里面的逻辑就是打印处理被拒绝的任务内容
public class MyRejectPolicy implements RejectedExecutionHandler{
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//Sender是我的Runnable类,里面有message字段
if (r instanceof Sender) {
Sender sender = (Sender) r;
//直接打印
System.out.println(sender.getMessage());
}
}
}
网友评论