设置线程池的大小
线程池的理想大小取决于被提交任务的类型以及所部署的系统。通常根据某种配置机制来提供。
对于计算密集型的任务,在拥有个处理器的系统上,当线程池的大小为
时,通常能实现最优的利用率。
对于包含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并发编程实战》