目录
2.3.1.int corePoolSize => 该线程池中核心线程数最大值
2.3.2.int maximumPoolSize => 该线程池中线程总数最大值
2.3.3.long keepAliveTime => 该线程池中非核心线程闲置超时时长
2.3.4.TimeUnit unit => keepAliveTime 的单位
2.3.5.BlockingQueue workQueue => 该线程池中的任务队列
2.3.6.ThreadFactory threadFactory
2.3.7.RejectedExecutionHandler handler
一.线程形态
1.1.HandlerThread
1.1.1.简介
HandlerThread继承了Thread,是一个包含 Looper 的消息循环的 Thread,我们可以直接使用这个 Looper 创建 Handler。在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,避免在子线程中对Looper手动繁琐的操作(Looper.prepare() ——> Looper.loop()),让我们可以直接在线程中使用 Handler 来处理异步任务。
(1)HandlerThread本身是一个Thread,需要start()启动。
(2)HandlerThread在run() 方法中为本线程创建了Looper(Looper.prepare()),调用 onLooperPrepared 后开启了循环(Looper.loop())
(3)HandlerThread需要在子类中重写 onLooperPrepared,做Looper启动前的初始化工作
(4)HandlerThread可以指定优先级,注意这里的参数是 Process.XXX 而不是 Thread.XXX
(5)HandlerThread = Thread + Looper,适合在子线程中执行耗时的、可能有多个任务的操作的场景,比如说多个网络请求操作,或者多文件 I/O 等等。
个人理解:HandlerThread是一个可以通过Looper + Message 实现在子线程(Looper 和 MessageQueue在子线程,所以Looper循环消息,触发消息处理方法也是在子线程)中依次循环执行任务的线程类。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* 子类需要重写的方法,在这里做一些执行前的初始化工作
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
//调用 start() 后就会执行的 run()
@Override
public void run() {
mTid = Process.myTid();
//创建了 Looepr
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();//Looper 已经创建,唤醒阻塞在获取 Looper 的线程
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();//开始循环
mTid = -1;
}
/**
* 获取当前线程的 Looper
* 如果线程不是正常运行的就返回 null
* 如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
/**
* Quits the handler thread's looper.
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
1.1.2.适用场景
为某个任务 / 回调单独开启线程,并提供由Handler + Looper组成的任务(Message)调度机制。HandlerThread = Thread + Looper,适合在子线程中执行耗时的、可能有多个任务的操作的场景,比如说多个网络请求操作,或者多文件 I/O 等等。
1.1.3.DEMO
使用 HandlerThread 实现子线程完成多个下载任务。
DownloadThread,它有两个 Handler 类型的成员变量,一个是用于在子线程传递、执行任务,另一个用于外部传入,在主线程显示下载状态:
public class DownloadThread extends HandlerThread implements Handler.Callback {
private final String KEY_URL = "url";
public static final int TYPE_START = 1;
public static final int TYPE_FINISHED = 2;
/**
* 外部传入,通知主线程显示下载状态
*/
private Handler mUIHandler;
/**
* 内部创建,子线程传递、执行任务
*/
private Handler mWorkerHandler;
/**
* download list
*/
private List<String> mDownloadUrlList;
public DownloadThread(final String name) {
super(name);
}
/**
* 执行初始化任务
*/
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mWorkerHandler = new Handler(getLooper(), this); //使用子线程中的 Looper
if (mUIHandler == null) {
throw new IllegalArgumentException("Not set UIHandler!");
}
// 将接收到的任务消息挨个添加到消息队列中
for (String url : mDownloadUrlList) {
Message message = mWorkerHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString(KEY_URL, url);
message.setData(bundle);
mWorkerHandler.sendMessage(message);
}
}
public void setDownloadUrls(String... urls) {
mDownloadUrlList = Arrays.asList(urls);
}
/**
* 获取主线程 Handler
*/
public Handler getUIHandler() {
return mUIHandler;
}
/**
* 注入主线程 Handler
*/
public DownloadThread setUIHandler(final Handler UIHandler) {
mUIHandler = UIHandler;
return this;
}
/**
* 子线程中执行任务,完成后发送消息到主线程
*/
@Override
public boolean handleMessage(final Message msg) {
if (msg == null || msg.getData() == null) {
return false;
}
String url = (String) msg.getData().get(KEY_URL);
//下载开始,通知主线程
Message startMsg = mUIHandler.obtainMessage(TYPE_START, "\n 开始下载 @" + url);
mUIHandler.sendMessage(startMsg);
SystemClock.sleep(2000); //模拟下载
//下载完成,通知主线程
Message finishMsg = mUIHandler.obtainMessage(TYPE_FINISHED, "\n 下载完成 @" + url);
mUIHandler.sendMessage(finishMsg);
return true;
}
@Override
public boolean quitSafely() {
mUIHandler = null;
return super.quitSafely();
}
}
创建一个子线程 mWorkerHandler,在 onLooperPrepared()中初始化 Handler,使用的是 HandlerThread 创建的 Looper 。同时将外部传入的下载 url 以 Message 的方式发送到子线程中的 MessageQueue 中。
当调用 DownloadThread.start() 时,子线程中的 Looper 开始工作,会按顺序取出消息队列中的队列处理,然后调用子线程的 Handler 处理(handleMessage()),在这个方法中进行耗时任务,然后通过 mUIHandler 将下载状态信息传递到主线程
在外部Activity(Fragment)实现Hander.Callback接口,调用:
mUIHandler = new Handler(this);
mDownloadThread = new DownloadThread("下载线程");
mDownloadThread.setUIHandler(mUIHandler);
mDownloadThread.setDownloadUrls("http://pan.baidu.com/s/1qYc3EDQ",
"http://bbs.005.tv/thread-589833-1-1.html",
"http://list.youku.com/show/id_zc51e1d547a5b11e2a19e.html?");
mDownloadThread.start();
同时在Activiy的onDestroy()方法中:
mDownloadThread.quitSafely();
1.2.IntentService
1.2.1.简介
IntentService 是一个服务,同时是一个抽象类,继承自普通Service同时又在内部创建了一个HandlerThread 。IntentService 使用工作线程逐一处理所有启动请求。如果你不需要在 Service 中执行并发任务,IntentService 是最好的选择。
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
// 内部创建的 Handler
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 调用这个方法处理数据
onHandleIntent((Intent) msg.obj);
// 处理完Service就自尽了
stopSelf(msg.arg1);
}
}
// 子类需要重写的构造函数,参数是服务的名称
public IntentService(String name) {
super();
mName = name;
}
// 设置当前服务被意外关闭后是否重新启动
// 如果设置为 true,onStartCommand() 方法将返回 Service.START_REDELIVER_INTENT,这样当
// 当前进程在 onHandleIntent() 方法返回前销毁时,会重启进程,重新使用之前的 Intent 启动这个服务
// (如果有多个 Intent,只会使用最后的一个)
// 如果设置为 false,onStartCommand() 方法返回 Service.START_NOT_STICKY,当进程销毁后也不重启服务
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
// 创建时启动一个 HandlerThread
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 拿到 HandlerThread 中的 Looper,然后创建一个子线程中的 Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
// 将 intent 和 startId 以消息的形式发送到 Handler
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
*
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit(); //值得学习的,在销毁时退出 Looper
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
(1)创建了一个 HandlerThread 为默认的工作线程
(2)使用 HandlerThread 的 Looper 创建了一个 Handler,这个 Handler 执行在子线程
(3)在 onStartCommand() 中调用 onStart(),然后在 onStart() 中将 intent 和 startId 以消息的形式发送到 ServiceHandler
(4)在 ServiceHandler处理消息时(handleMessage),将消息中的msg.obj(Intent)按顺序传递给 onHandleIntent() 方法
(5)在处理完所有启动请求后自动停止服务,不需要我们调用 stopSelf()
那么在 handleMessage 方法中不是调用了一次 onHandleIntent() 后就调用 stopSelf() 了吗,这不是只能执行一个任务么?
stopSelf() 方法传递了一个 id,这个 id 是启动服务时 IActivityManager 分配的 id,当我们调用 stopSelf(id) 方法结束服务时,IActivityManager 会对比当前 id 是否为最新启动该服务的 id,如果是就关闭服务。
子线程中onHandleIntent方法经历了耗时操作,当本次stopSelf(id)调用之前,很可能onStartCommand会多次调用,那么本次传入的id,已经不是最新启动服务的id了。
因此只有当最后一次启动 IntentService 的任务执行完毕才会关闭这个服务。
注意:只有onHandleIntent方法是执行在子线程的,因为处理消息的looper是子线程HandlerThread提供的looper。其余方法均在主线程运行。
1.2.2.适用场景
IntentService可用于执行后台耗时的任务,当任务执行后它会自动停止,同时由于IntentService是服务的原因,这导致他的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务。
1.2.3.DEMO
1.多文件下载:
由于最终每个任务的处理都会调用 onHandleIntent(),因此使用 IntentService 也很简单,只需实现 onHandleIntent() 方法,在这里执行对应的后台工作即可。
/**
* Description:使用 IntentService 实现下载
*/
public class DownloadService extends IntentService {
private static final String TAG = "DownloadService";
public static final String DOWNLOAD_URL = "down_load_url";
public static final int WHAT_DOWNLOAD_FINISHED = 1;
public static final int WHAT_DOWNLOAD_STARTED = 2;
public DownloadService() {
super(TAG);
}
private static Handler mUIHandler;
public static void setUIHandler(final Handler UIHandler) {
mUIHandler = UIHandler;
}
/**
* 这个方法运行在子线程
*
* @param intent
*/
@Override
protected void onHandleIntent(final Intent intent) {
String url = intent.getStringExtra(DOWNLOAD_URL);
if (!TextUtils.isEmpty(url)) {
sendMessageToMainThread(WHAT_DOWNLOAD_STARTED, "\n " + DateUtils.getCurrentTime() + " 开始下载任务:\n" + url);
try {
Bitmap bitmap = downloadUrlToBitmap(url);
SystemClock.sleep(1000); //延迟一秒发送消息
sendMessageToMainThread(WHAT_DOWNLOAD_FINISHED, bitmap);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 发送消息到主线程
*
* @param id
* @param o
*/
private void sendMessageToMainThread(final int id, final Object o) {
if (mUIHandler != null) {
mUIHandler.sendMessage(mUIHandler.obtainMessage(id, o));
}
}
/**
* 下载图片
*
* @param url
* @return
* @throws Exception
*/
private Bitmap downloadUrlToBitmap(String url) throws Exception {
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
BufferedInputStream in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
Bitmap bitmap = BitmapFactory.decodeStream(in);
urlConnection.disconnect();
in.close();
return bitmap;
}
}
在 onHandleIntent() 中接收任务,开始下载,同时将状态返回给主线程
Activity主调代码:
public void downloadImage() {
DownloadService.setUIHandler(new Handler(this));
Intent intent = new Intent(this, DownloadService.class);
for (String url : urlList) {
intent.putExtra(DownloadService.DOWNLOAD_URL, url);
startService(intent);
}
mBtnDownload.setEnabled(false);
}
主线程中:
(1)设置 UI 线程的 Handler 给 IntentService
(2)使用 startService(intent) 启动 IntentService 执行图片下载任务
(3)在 Handler 的 handleMessage 中根据消息类型进行相应处理
在第一次启动 IntentService 后,IntentService 仍然可以接受新的请求,接受到的新的请求被放入了工作队列中,等待被串行执行。
2.使用广播更新UI:
public class MyIntentService extends IntentService {
/**
* 是否正在运行
*/
private boolean isRunning;
/**
*进度
*/
private int count;
/**
* 广播
*/
private LocalBroadcastManager mLocalBroadcastManager;
public MyIntentService() {
super("MyIntentService");
Logout.e("MyIntentService");
}
@Override
public void onCreate() {
super.onCreate();
Logout.e("onCreate");
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
}
@Override
protected void onHandleIntent(Intent intent) {
Logout.e("onHandleIntent");
try {
Thread.sleep(1000);
isRunning = true;
count = 0;
while (isRunning) {
count++;
if (count >= 100) {
isRunning = false;
}
Thread.sleep(50);
sendThreadStatus("线程运行中...", count);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 发送进度消息
*/
private void sendThreadStatus(String status, int progress) {
Intent intent = new Intent(IntentServiceActivity.ACTION_TYPE_THREAD);
intent.putExtra("status", status);
intent.putExtra("progress", progress);
mLocalBroadcastManager.sendBroadcast(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Logout.e("线程结束运行..." + count);
}
}
1.2.4.总结
IntentService是一个串行执行异步任务、会自尽的 Service,优先级比较高,在后台不会轻易被系统杀死;它可以接收多个 Intent 请求,然后在子线程中按顺序执行。
适合于执行由UI触发的处理多任务的后台Service任务,并可以把后台任务执行的情况通过一定的机制反馈给UI。
IntentService,在onHandlerIntent()的回调里面来处理塞到IntentService的任务。所以IntentService就不仅仅具备了异步线程的特性,还同时保留了Service不受主页面生命周期影响的特点。我们可以在IntentService里面通过设置闹钟间隔性的触发异步任务,例如刷新数据,更新缓存的图片等。
首先,因为IntentService内置的是HandlerThread作为异步线程,所以每一个交给IntentService的任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
其次,通常使用到IntentService的时候,我们会结合使用BroadcastReceiver把工作线程的任务执行结果返回给主UI线程。使用广播容易引起性能问题,我们可以使用LocalBroadcastManager来发送只在程序内部传递的广播,从而提升广播的性能。我们也可以使用runOnUiThread()快速回调到主UI线程。
最后,包含正在运行的IntentService的程序相比起纯粹的后台程序更不容易被系统杀死,该程序的优先级是介于前台程序与纯后台程序之间的。
1.3.RxAsyncTask
利用RxJava重新实现异步任务:
public abstract class RxAsyncTask<Param, Progress, Result> {
private final String TAG = this.getClass().getSimpleName();
private Flowable<Progress[]> mFlowable;
public RxAsyncTask() {
}
@SafeVarargs
private final void rxTask(final Param... params) {
Flowable.create(new FlowableOnSubscribe<Result>() {
@Override
public void subscribe(FlowableEmitter<Result> e) throws Exception {
e.onNext(RxAsyncTask.this.call(params));
e.onComplete();
}
}, BackpressureStrategy.BUFFER).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Result>() {
@Override
public void onSubscribe(Subscription s) {
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Result result) {
RxAsyncTask.this.onResult(result);
}
@Override
public void onError(Throwable t) {
RxAsyncTask.this.onError(t);
}
@Override
public void onComplete() {
RxAsyncTask.this.onCompleted();
}
});
}
protected abstract Result call(Param... params);
/**
* 任务开始之前调用(在当前调用者所在线程执行)
*/
protected void onPreExecute() {
}
/**
* 执行结果返回
*/
protected void onResult(Result result) {
}
/**
* 进度更新
*/
protected void onProgressUpdate(Progress... progresses) {
}
/**
* RxJava中的onComplete回调
*/
protected void onCompleted() {
}
/**
* RxJava中的onError回调
*/
protected void onError(Throwable e) {
}
/**
* 进度更新 子线程转主线程 回调给 onProgressUpdate()方法
*/
@SuppressLint("CheckResult")
protected void publishProgress(final Progress... progresses) {
if (mFlowable == null) {
mFlowable = Flowable.create(new FlowableOnSubscribe<Progress[]>() {
@Override
public void subscribe(FlowableEmitter<Progress[]> e) throws Exception {
e.onNext(progresses);
}
}, BackpressureStrategy.BUFFER).observeOn(AndroidSchedulers.mainThread());
}
mFlowable.subscribe(new Consumer<Progress[]>() {
@Override
public void accept(Progress[] progress) throws Exception {
onProgressUpdate(progresses);
}
});
}
@SafeVarargs
public final void execute(Param... params) {
onPreExecute();
rxTask(params);
}
}
二.线程池ThreadPoolExecutor
2.1.为什么要用线程池
1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率
例如:
记创建线程消耗时间 T1,执行任务消耗时间 T2,销毁线程消耗时间 T3,如果 T1+T3>T2,那么是不是说开启一个线程来执行这个任务太不划算了!
线程池缓存线程,可用已有的闲置线程来执行新任务,避免了 T1+T3 带来的系统开销。
2.线程并发数量过多,抢占系统资源从而导致阻塞
我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况。运用线程池能有效的控制线程最大并发数,避免以上的问题。
3.对线程进行一些简单的管理
比如:延时执行、定时循环执行的策略等,运用线程池都能进行很好的实现。
大多数实际场景中是这样的:处理某一次请求的时间是非常短暂的,但是请求数量是巨大的。这种技术背景下,如果我们为每一个请求都单独创建一个线程,那么物理机的所有资源基本上都被操作系统创建线程、切换线程状态、销毁线程这些操作所占用,用于业务请求处理的资源反而减少了。所以最理想的处理方式是,将处理请求的线程数量控制在一个范围,既保证后续的请求不会等待太长时间,又保证物理机将足够的资源用于请求处理本身。
另外,一些操作系统是有最大线程数量限制的。当运行的线程数量逼近这个值的时候,操作系统会变得不稳定。这也是我们要限制线程数量的原因。
2.2.线程池逻辑构成
Java语言为我们提供了两种基础线程池的选择:ScheduledThreadPoolExecutor和ThreadPoolExecutor。它们都实现了ExecutorService接口(注意,ExecutorService接口本身和“线程池”并没有直接关系,它的定义更接近“执行器”)。
线程池逻辑结构:
构成线程池的几个重要元素:
等待队列:顾名思义,就是你调用线程池对象的submit()方法或者execute()方法,要求线程池运行的任务(这些任务必须实现Runnable接口或者Callable接口)。但是出于某些原因线程池并没有马上运行这些任务,而是送入一个队列等待执行。
核心线程:线程池主要用于执行任务的是“核心线程”,“核心线程”的数量是你创建线程时所设置的corePoolSize参数决定的。如果不进行特别的设定,线程池中始终会保持corePoolSize数量的线程数(不包括创建阶段)。
非核心线程:一旦任务数量过多(由等待队列的特性决定),线程池将创建“非核心线程”临时帮助运行任务。你设置的大于corePoolSize参数小于maximumPoolSize参数的部分,就是线程池可以临时创建的“非核心线程”的最大数量。这种情况下如果某个线程没有运行任何任务,在等待keepAliveTime时间后,这个线程将会被销毁,直到线程池的线程数量重新达到corePoolSize。
2.3.线程池参数
在 Java 中,线程池的概念是 Executor 这个接口,具体实现为 ThreadPoolExecutor类。对线程池的配置,就是对 ThreadPoolExecutor 构造函数的参数的配置。
ThreadPoolExecutor提供了四个构造方法,共7个可选参数:
2.3.1.int corePoolSize => 该线程池中核心线程数最大值
核心线程:
线程池新建线程的时候,如果当前线程总数小于 corePoolSize,则新建的是核心线程,如果超过 corePoolSize,则新建的是非核心线程。
核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态)。
如果指定 ThreadPoolExecutor 的 allowCoreThreadTimeOut 这个属性为 true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉。
很好理解吧,正常情况下你不干活我也养你,因为我总有用到你的时候,但有时候特殊情况(比如我自己都养不起了),那你不干活我就要把你干掉了。
2.3.2.int maximumPoolSize => 该线程池中线程总数最大值
线程总数 = 核心线程数 + 非核心线程数。
2.3.3.long keepAliveTime => 该线程池中非核心线程闲置超时时长
一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉。如果设置allowCoreThreadTimeOut = true,则会作用于核心线程。
2.3.4.TimeUnit unit => keepAliveTime 的单位
TimeUnit 是一个枚举类型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天
2.3.5.BlockingQueue workQueue => 该线程池中的任务队列
维护着等待执行的Runnable对象
当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。
常用的 workQueue 类型:
(1)SynchronousQueue:
这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现“线程数达到了 maximumPoolSize 而不能新建线程”的错误,使用这个类型队列的时候,maximumPoolSize 一般指定成 Integer.MAX_VALUE,即无限大。
(2)LinkedBlockingQueue:
这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了 maximumPoolSize 的设定失效,因为总线程数永远不会超过 corePoolSize。
(3)ArrayBlockingQueue:
可以限定队列的长度,接收到任务的时候,如果没有达到 corePoolSize 的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了 maximumPoolSize,并且队列也满了,则发生错误。
(4)DelayQueue:
队列内元素必须实现 Delayed 接口,这就意味着你传进去的任务必须先实现 Delayed 接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
2.3.6.ThreadFactory threadFactory
创建线程的方式,这是一个接口,你 new 他的时候需要实现他的 Thread newThread(Runnable r) 方法,一般用不上。
2.3.7.RejectedExecutionHandler handler
抛出异常专用的,比如上面提到的两个错误发生了,就会由这个 handler 抛出异常,你不指定他也有个默认的。
2.4.使用
向ThreadPoolExecutor添加任务
new 一个 ThreadPoolExecutor之后通过 ThreadPoolExecutor.execute(Runnable command)方法 即可向线程池内添加一个任务。
ThreadPoolExecutor的策略
上面介绍参数的时候其实已经说到了ThreadPoolExecutor执行的策略,这里给总结一下,当一个任务被添加进线程池时:
1. 线程数量未达到 corePoolSize,则新建一个线程(核心线程)执行任务
2. 线程数量达到了 corePoolSize,则将任务移入队列等待
3. 队列已满,新建线程(非核心线程)执行任务
4. 队列已满,总线程数又达到了 maximumPoolSize,就会由上面 (RejectedExecutionHandler)抛出异常。
2.5.线程池等待队列
在使用ThreadPoolExecutor线程池的时候,需要指定一个实现了BlockingQueue接口的任务等待队列。在ThreadPoolExecutor线程池的API文档中,一共推荐了三种等待队列,它们是:SynchronousQueue、LinkedBlockingQueue和ArrayBlockingQueue。
队列和栈
队列:是一种特殊的线性结构,允许在线性结构的前端进行删除/读取操作;允许在线性结构的后端进行插入操作;这种线性结构具有“先进先出”的操作特点:
但是在实际应用中,队列中的元素有可能不是以“进入的顺序”为排序依据的。例如我们将要讲到的PriorityBlockingQueue队列。
栈:栈也是一种线性结构,但是栈和队列相比只允许在线性结构的一端进行操作,入栈和出栈都是在一端完成。
2.5.1.有限队列
SynchronousQueue:
这是一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量。翻译一下:这是一个内部没有任何容量的阻塞队列,任何一次插入操作的元素都要等待相对的删除/读取操作,否则进行插入操作的线程就要一直等待,反之亦然。
ArrayBlockingQueue:
一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
2.5.2.无限队列
LinkedBlockingQueue:
LinkedBlockingQueue是我们在ThreadPoolExecutor线程池中常用的等待队列。它可以指定容量也可以不指定容量。由于它具有“无限容量”的特性,所以我还是将它归入了无限队列的范畴(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)。
LinkedBlockingQueue的实现是基于链表结构,而不是类似ArrayBlockingQueue那样的数组。但实际使用过程中,不需要关心它的内部实现,如果指定了LinkedBlockingQueue的容量大小,那么它反映出来的使用特性就和ArrayBlockingQueue类似了。
LinkedBlockingDeque:
LinkedBlockingDeque是一个基于链表的双端队列。LinkedBlockingQueue的内部结构决定了它只能从队列尾部插入,从队列头部取出元素;但是LinkedBlockingDeque既可以从尾部插入/取出元素,还可以从头部插入元素/取出元素。
PriorityBlockingQueue:
PriorityBlockingQueue是一个按照优先级进行内部元素排序的无限队列。存放在PriorityBlockingQueue中的元素必须实现Comparable接口,这样才能通过实现compareTo()方法进行排序。优先级最高的元素将始终排在队列的头部;PriorityBlockingQueue不会保证优先级一样的元素的排序,也不保证当前队列中除了优先级最高的元素以外的元素,随时处于正确排序的位置。
LinkedTransferQueue:
LinkedTransferQueue也是一个无限队列,它除了具有一般队列的操作特性外(先进先出),还具有一个阻塞特性:LinkedTransferQueue可以由一对生产者/消费者线程进行操作,当消费者将一个新的元素插入队列后,消费者线程将会一直等待,直到某一个消费者线程将这个元素取走,反之亦然。
2.6.常见线程池
Java 通过 Executors 提供了四种线程池,这四种线程池都是直接或间接配置 ThreadPoolExecutor 的参数实现的。
2.6.1.CachedThreadPool
可缓存线程池:
1. 线程池内部没有核心线程,线程数无限制
2. 有空闲线程则复用空闲线程,若无空闲线程则新建线程
3. 一定程度减少频繁创建/销毁线程,减少系统开销
4. 空闲线程超时会被销毁
创建方法:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源代码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L,
TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
2.6.2.FixedThreadPool
定长线程池:
1. 可控制线程最大并发数(同时执行的线程数)
2. 该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲置状态超时而被销毁,超出的线程会在队列中等待
3.如果当前线程数小于核心线程数,并且也有闲置线程的时候提交了任务,这时也不会去复用之前的闲置线程,会创建新的线程去执行任务。如果当前执行任务数大于了核心线程数,大于的部分就会进入队列等待。等着有闲置的线程来执行这个任务。
创建方法:
// nThreads = 最大线程数,即maximunPoolSize
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(nThreads)
源代码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
2.6.3.ScheduledThreadPool
定长(核心线程)线程池:
支持定时及周期性任务执行。
1. 不仅设置了核心线程数,最大线程数也是 Integer.MAX_VALUE
2. 这个线程池是唯一个有延迟执行和周期执行任务的线程池
2.6.4.SingleThreadExecutor
单线程化的线程池:
1. 有且仅有一个工作线程执行任务
2. 所有任务按照指定顺序执行,即遵循队列的入队出队规则
创建方法:
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
源代码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}