Sử dụng ThreadPoolExecutor trong android
Bài đăng này đã không được cập nhật trong 8 năm
Đã làm việc nhiều với Thread và cả sử dụng Thread Pool nhưng chưa thực sự tạo được một reusable code. Tình cờ đọc được một cách hiện thực ThreadPoolExecutor khá hay, mong muốn được chia sẻ cùng mọi người 
1. Thread Pools
- Một
thread poolđơn giản là mộtpool(bể chứa) bao gồm nhiềuworker threads(Số lượngworker threadsẽ phụ thuộc vào cách màthread poolđược hiện thực). - Một Task Queue (hàng đợi nhiệm vụ) sẽ có nhiệm vụ chứa các
tasks. Một khi có 1Idle Thread(threadrãnh rỗi), nó sẽ vào trongQueuenhậntaskvà thực thi chúng. Lúc này cácIdle Threadkhác sẽ phải chờthreadtrước đó nhận xongtask.
2. ThreadPoolExecutor
ThreadPoolExecutor thực thi nhiệm vụ được giao bởi một trong những thread từ thread pool.
2.1 Lợi ích của việc sử dụng ThreadPoolExecutor
- Là một
powerful task execution framework.ThreadPoolExecutorhỗ trợ việc: thêmtaskvàoQueue(task addition), hủytask(task cancellation), và thực hiệntaskcó độ ưu tiên (task prioritization). - Giảm thiểu chi phí trong việc tạo quá nhiều
threadvìThreadPoolExecutorcó cơ chế quản lý số lượngthreadđược tạo ra trongthread pool.
2.2 ThreadPoolExecutor trong android
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
);
- corePoolSize: Số lượng
threadít nhất chứa được giữ trongpool, kể cả khi cácthreadđều ở trạng tháiIdle. Khi khởi tạo, số lượngthreadcó thể là 0. Khitaskđược thêm vào thìthreadmới được tạo ra. Kể từ đây, nếu số lượngthreadít hơncorePoolSizethì nhữngthreadmới sẽ được tạo ra đến khi sốthreadbằng giá trị củacorePoolSize. - maximumPoolSize: Số lượng
threadnhiều nhất có thể chứa trongpool. - keepAliveTime: Khi số
threadlớn hơn core,keepAliveTimelà thời gian tối đa mà 1Idle threadchờtask. Khi hết thời gian chờ mà chưa cótask,threadnày sẽ bị hủy. - unit: Đơn vị thời gian của
keepAliveTime - workQueue: là hàng đợi dùng để chứa/giữ các
Tasktrước khi chúng được thực thi. Hàng đợi này chỉ chứaRunnable taskđược đăng kí bởi phương thức (hàm)execute(). Trong android, hàng đợi này là một BlockingQueue.
3. Sử dụng ThreadPoolExecutor
Phần này sẽ trình bày cách hiện thực và sử dụng ThreadPoolExcutor một cách hiệu quả từ việc tạo, thực thi, hủy và thực thi có độ ưu tiên task.
3.1 Khởi tạo và thực thi
MainThreadExecutor
Class này đảm nhiệm việc tạo task trong Main Thread.
public class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable runnable) {
handler.post(runnable);
}
}
PriorityThreadFactory
Class này sẽ đóng vai trò phân biệt Executor cho Main Thread tasks hay Background Thread task bằng phương thức Process.setThreadPriority(int priority).
VD: Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
public class PriorityThreadFactory implements ThreadFactory {
// priority of thread based on Linux priorities
// A Linux priority level, from -20 for highest scheduling priority to 19 for lowest scheduling priority.
private final int mThreadPriority;
public PriorityThreadFactory(int threadPriority) {
mThreadPriority = threadPriority;
}
@Override
public Thread newThread(final Runnable runnable) {
Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
try {
Process.setThreadPriority(mThreadPriority);
} catch (Throwable t) {
}
runnable.run();
}
};
return new Thread(wrapperRunnable);
}
}
DefaultExecutorSupplier
Singleton class giúp khởi tạo các ThreadPoolExecutor khác nhau cho những task khác nhau.
public class DefaultExecutorSupplier {
// Number of cores to decide the number of threads
public static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
// thread pool executor for background tasks
private final ThreadPoolExecutor mForBackgroundTasks;
// thread pool executor for light weight background tasks
private final ThreadPoolExecutor mForLightWeightBackgroundTasks;
// thread pool executor for main thread tasks
private final Executor mMainThreadExecutor;
// an instance of DefaultExecutorSupplier
private static DefaultExecutorSupplier sInstance;
// returns the instance of DefaultExecutorSupplier
public static DefaultExecutorSupplier getInstance() {
if (sInstance == null) {
synchronized(DefaultExecutorSupplier.class){
sInstance = new DefaultExecutorSupplier();
}
return sInstance;
}
// constructor for DefaultExecutorSupplier
private DefaultExecutorSupplier() {
// setting the thread factory
ThreadFactory backgroundPriorityThreadFactory = new
PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND);
// setting the thread pool executor for mForBackgroundTasks;
mForBackgroundTasks = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
backgroundPriorityThreadFactory
);
// setting the thread pool executor for mForLightWeightBackgroundTasks;
mForLightWeightBackgroundTasks = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
backgroundPriorityThreadFactory
);
// setting the thread pool executor for mMainThreadExecutor;
mMainThreadExecutor = new MainThreadExecutor();
}
// returns the thread pool executor for background task
public ThreadPoolExecutor forBackgroundTasks() {
return mForBackgroundTasks;
}
// returns the thread pool executor for light weight background task
public ThreadPoolExecutor forLightWeightBackgroundTasks() {
return mForLightWeightBackgroundTasks;
}
// returns the thread pool executor for main thread task
public Executor forMainThreadTasks() {
return mMainThreadExecutor;
}
}
Execute task
Sử dụng những ThreadPoolExecutor khác nhau để thực thi những loại task khác nhau:
// Using it for Background Tasks
public void doSomeBackgroundWork(){
DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.execute(new Runnable() {
// Executes the given task sometime in the future.
@Override
public void run() {
// do some background work here.
}
});
}
// Using it for Light-Weight Background Tasks
public void doSomeLightWeightBackgroundWork(){
DefaultExecutorSupplier.getInstance().forLightWeightBackgroundTasks()
.execute(new Runnable() {
@Override
public void run() {
// do some light-weight background work here.
}
});
}
// Using it for MainThread Tasks
public void doSomeMainThreadWork(){
DefaultExecutorSupplier.getInstance().forMainThreadTasks()
.execute(new Runnable() {
@Override
public void run() {
// do some Main Thread work here.
}
});
}
Với cách này chúng ta có sử dụng những Thread pool khác nhau cho những mục đích khác nhau: Network tasks, I/O tasks, heavy background tasks...
3.2 Cancel task
Thay vì dùng hàm execute, ta dùng hàm submit và sử dụng nó để hủy task.
VD:
// Get the future of the task by submitting it to the pool
Future future = DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.submit(new Runnable() {
@Override
public void run() {
// do some background work here.
}
});
...
// Cancelling the task
future.cancel(true);
3.3 Set độ ưu tiên cho task
Giả sử chúng ta có nhiều task, nhưng thread pool chỉ chứa hữu hạn số lượng các thread, vì vậy việc thực thi task theo độ ưu tiên có ý nghĩa rất lớn.
VD: Bạn muốn query data ngay lập tức, muốn download dữ liệu nào đó trước khi save data...
Trước hết tạo một enum các độ ưu tiên mong muốn:
public enum Priority {
/**
* NOTE: DO NOT CHANGE ORDERING OF THOSE CONSTANTS UNDER ANY CIRCUMSTANCES.
* Doing so will make ordering incorrect.
*/
// Lowest priority level. Used for prefetches of data.
LOW,
// Medium priority level. Used for warming of data that might soon get visible.
MEDIUM,
// Highest priority level. Used for data that are currently visible on screen.
HIGH,
// Highest priority level. Used for data that are required instantly(mainly for emergency).
IMMEDIATE;
}
Tiếp theo tạo một Runnable chứa độ ưu tiên
public class PriorityRunnable implements Runnable {
private final Priority priority;
public PriorityRunnable(Priority priority) {
this.priority = priority;
}
@Override
public void run() {
// nothing to do here.
}
public Priority getPriority() {
return priority;
}
}
Tạo ThreadPoolExecutor có xử lý độ ưu tiên:
Lưu ý hàm compareTo(PriorityFutureTask other) dùng để so sánh độ ưu tiên của 2 task khác nhau.
public class PriorityThreadPoolExecutor extends ThreadPoolExecutor {
public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit,new PriorityBlockingQueue<Runnable>(), threadFactory);
}
@Override
public Future<?> submit(Runnable task) {
PriorityFutureTask futureTask = new PriorityFutureTask((PriorityRunnable) task);
execute(futureTask);
return futureTask;
}
private static final class PriorityFutureTask extends FutureTask<PriorityRunnable>
implements Comparable<PriorityFutureTask> {
private final PriorityRunnable priorityRunnable;
public PriorityFutureTask(PriorityRunnable priorityRunnable) {
super(priorityRunnable, null);
this.priorityRunnable = priorityRunnable;
}
/*
* compareTo() method is defined to compare 2 tasks with their priority.
*/
@Override
public int compareTo(PriorityFutureTask other) {
Priority p1 = priorityRunnable.getPriority();
Priority p2 = other.priorityRunnable.getPriority();
return p2.ordinal() - p1.ordinal();
}
}
}
Thay đổi 1 chút trong DefaultExecutorSupplier (ở trên)
Thay vì dùng ThreadPoolExecutor, ta sẽ dùng PriorityThreadPoolExecutor
public class DefaultExecutorSupplier{
private final PriorityThreadPoolExecutor mForBackgroundTasks;
private DefaultExecutorSupplier() {
mForBackgroundTasks = new PriorityThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2,
60L,
TimeUnit.SECONDS,
backgroundPriorityThreadFactory
);
...
}
}
Cuối cùng thực thi một task với độ ưu tiên mà bạn mong muốn 
//do some task at high priority
public void doSomeTaskAtHighPriority(){
DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.submit(new PriorityRunnable(Priority.HIGH) {
@Override
public void run() {
// do some background work here at high priority.
}
});
}
Reference:
https://blog.mindorks.com/threadpoolexecutor-in-android-8e9d22330ee3#.pjemmk4t7 https://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html https://github.com/amitshekhariitbhu/Fast-Android-Networking/tree/master/android-networking/src/main/java/com/androidnetworking/core
All rights reserved