設置線程池的大小
線程池的理想大小取決於被提交任務的類型以及所部署的系統。通常根據某種配置機制來提供。
對於計算密集型的任務,在擁有個處理器的系統上,當線程池的大小爲時,通常能實現最優的利用率。
對於包含I/O操作或者其他阻塞操作的任務,由於線程並不會一直執行,因此線程池的規模應該更大。
=number of CPUs
=target CPU utilization, 0 ≤≤1
=ratio of wait time to compute time
要使處理器達到期望的使用率,線程池的最優大小等於:
可以通過Runtime獲得CPU的數目:
int N_CPU = Runtime.getRuntime().availableProcessors();
配置ThreadPoolExecutor
ThreadPoolExecutor爲一些Executor提供了基本的實現:Executor框架與Java線程池
飽和策略
當有界隊列被填滿後,飽和策略開始發揮作用。通過setRejectedExecutionHandler來修改。
AbortPolicy: 默認策略,拋出未檢查的RejectedExecutionException供調用者捕獲。
DiscardPolicy: 悄悄拋棄該任務。
DiscardOldestPolicy: 拋棄下一個將被執行的任務(對於優先隊列,將是優先級最高的那個任務),然後嘗試重新提交新的任務。
CallerRunsPolicy: 既不拋棄任務,也不拋出異常,而是將某些任務回退到調用者,從而降低新任務的流量。當工作隊列被填滿後,下一個任務會在調用execute時在主線程中執行。由於執行任務需要一定的時間,因此主線程至少在一段時間內不能提交任務,從而使得工作者線程有時間來處理完正在執行的任務。
示例代碼:
ThreadPoolExecutor executor = new ThreadPoolExecutor(N_THREADS, N_THREADS, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(CAPACITY));
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
使用Semaphore限制任務的到達率:
public class BoundedExecutor {
private final Executor exec;
private final Semaphore semaphore;
public BoundedExecutor(Executor exec, int bound) {
this.exec = exec;
this.semaphore = new Semaphore(bound);
}
public void submitTask(final Runnable command) throws InterruptedException {
semaphore.acquire();
try {
exec.execute(new Runnable() {
@Override
public void run() {
try {
command.run();
} finally {
semaphore.release();
}
}
});
} catch (Exception e) {
semaphore.release();
}
}
}
每當線程需要創建一個線程時,都是通過線程工廠方法來完成的。可以爲每個線程指定UncaughtExceptionHandler,或者實例化一個定製的Thread類用於執行調試信息的記錄:
public class MyThreadFactory implements ThreadFactory {
private final String poolName;
public MyThreadFactory(String poolName) {
this.poolName = poolName;
}
@Override
public Thread newThread(Runnable r) {
return new MyAppThread(r, poolName);
}
}
public class MyAppThread extends Thread {
public static final String DEFAULT_NAME = "MyAppThread";
private static final AtomicInteger created = new AtomicInteger();
private static final AtomicInteger alive = new AtomicInteger();
private static final Logger logger = Logger.getAnonymousLogger();
private static volatile boolean debugLifeCycle = false;
public MyAppThread(Runnable target) {
this(target, DEFAULT_NAME);
}
public MyAppThread(Runnable target, String name) {
super(target, name + "-" + created.incrementAndGet());
setUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
logger.log(Level.SEVERE, "UNCAUGHT in thread " + t.getName(), e);
}
}
);
}
@Override
public void run() {
//複製debug標誌以確保一致的值??
boolean debug = debugLifeCycle;
if (debug) logger.log(Level.FINE, "Created " + getName());
try {
alive.incrementAndGet();
super.run();
} finally {
alive.decrementAndGet();
if (debug) logger.log(Level.FINE, "Exiting " + getName());
}
}
public static int getThreadCreated() { return created.get();}
public static int getThreadAlive() { return alive.get();}
public static boolean getDebug() { return debugLifeCycle; }
public static void setDebug(boolean debug) { debugLifeCycle = debug;}
}
擴展ThreadPoolExecutor
ThreadPoolExecutor是可以擴展的,它提供了幾個可以在子類中改寫的方法:beforeExecute、afterExecute和terminated,這些方法可以用於擴展ThreadPoolExecutor的行爲。
示例代碼:
public class TimingThreadPool extends ThreadPoolExecutor {
private final ThreadLocal<Long> startTime = new ThreadLocal<Long>();
private final Logger logger = Logger.getLogger("TimingThreadPool");
private final AtomicLong numTasks = new AtomicLong();
private final AtomicLong totalTime = new AtomicLong();
public TimingThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
logger.fine(String.format("Thread %s: start %s", t, r));
startTime.set(System.nanoTime());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
try {
long endTime = System.nanoTime();
long taskTime = endTime - startTime.get();
numTasks.incrementAndGet();
totalTime.addAndGet(taskTime);
logger.fine(String.format("Thread %s: end %s, time = %dns", t, r, taskTime));
} finally {
super.afterExecute(r, t);
}
}
@Override
protected void terminated() {
try {
logger.info(String.format("Terminated: avg time=%dns", totalTime.get() / numTasks.get()));
} finally {
super.terminated();
}
}
}
參考:《Java併發編程實戰》