1、线程饥饿死锁
在线程池中,如果任务依赖于其他任务,那么可能发生死锁。在单线程的Executor中,如果一个任务将另一个任务提交到同一个Executor,并且等待这个被提交任务的结果,那么通常会引发死锁。
如下面代码所示:
public class ThreadDeadlock {
ExecutorService exec = Executors.newSingleThreadExecutor();//使用单线程的Executor
public class LoadFileTask implements Callable<String> {
private final String fileName;
public LoadFileTask(String fileName) {
this.fileName = fileName;
}
public String call() throws Exception {
// Here's where we would actually read the file
return "";
}
}
public class RenderPageTask implements Callable<String> {
public String call() throws Exception {
Future<String> header, footer;
header = exec.submit(new LoadFileTask("header.html"));
footer = exec.submit(new LoadFileTask("footer.html"));
String page = renderBody();
System.out.println("提交子任务");
// Will deadlock -- task waiting for result of subtask
return header.get() + page + footer.get();
}
private String renderBody() {
// Here's where we would actually render the page
return "";
}
}
public static void main(String[] args) {
ThreadDeadlock deadLock = new ThreadDeadlock();
ThreadDeadlock.RenderPageTask t = deadLock.new RenderPageTask();
Future<String> result = deadLock.exec.submit(t);
System.out.println("提交主任务");
try {
System.out.println("获取结果");
result.get();
System.out.println("===结束===");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
运行结果:
2、设置线程池的大小
如果线程池过大,大量的线程将在相对较少的CUP和内存资源上发生竞争,这不仅会导致更高的内存使用量,而且还可能耗尽资源。如果线程池过小,那么将导致许多空闲的处理器无法执行工作,从而降低吞吐率。
3、配置ThreadPoolExecutor
(1)参数简介
ThreadPoolExecutor实现了ExecutorService和Executors接口,通常可通过Executors
工厂方法来配置参数。较为完整的构造器如下所示。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit timeUnit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler rejectedExecutionHandler)
corePoolSize:核心线程数(线程池努力去保持的一个线程数量)
maximumPoolSize:最大线程数
keepAliveTime:线程空闲时间。当线程数>corePoolSize,空闲线程最多等待keepAliveTime时间,便会销毁。
timeUnit :keepAliveTime的时间单位
workQueue:任务队列,保存等待执行的任务。
threadFactory :用来创建新线程的工厂
rejectedExecutionHandler :任务拒绝处理器。当拒绝任务时会触发
执行顺序:
①线程数<corePoolSize,即使有其他空闲的工作线程,也会创建一个新线程来处理请求。
②如果线程数>=corePoolSize,会将新任务放在队列中等待
③如果队列满了,且线程数<maximumPoolSize,才会创建一个新线程来处理请求。
④如果队列满了,且线程数>=maximumPoolSize,拒绝请求
如果将corePoolSize与maximumPoolSize设置成一样,那么会的到一个固定大小的线程池。
通过设置maximumPoolSize为一个无限大的数值(如Integer.MAX_VALUE),该线程池能适应任意数量的并发任务。
(2)管理队列任务
ThreadPoolExecutor可以提供一个BlockingQueue来保存等待执行的任务。基本的任务排队方法有三种:无界队列、有界队列和同步移交。
只有当任务相互独立时,为线程池或工作队列设置界限才是合理的。如果任务之间存在依赖性,那么有界的线程池或队列就可能导致线程“饥饿”死锁问题。此时应该使用无界的线程池,如:newCachedThreadPool。
(3)任务拒绝处理策略(RejectedExecutionHandler )
当任务添加到线程池中被拒绝时:
①ThreadPoolExecutor.AbortPolicy。会抛出RejectedExecutionException异常
②ThreadPoolExecutor.CallerRunsPolicy。将某些任务回退到调用者,从而降低新任务的流量。它不会在线程池的某个线程中执行新提交的任务,而是在一个调用了execute的线程中执行该任务。
③ThreadPoolExecutor.DiscardPolicy。无法执行的任务会被简单地抛弃
④ThreadPoolExecutor.DiscardOldestPolicy。如果executor没有关闭,在任务队列头的任务会被抛弃,然后会再次尝试重新提交新的任务(可能会再次失败,继续重复这个操作)
(4)线程工厂(ThreadFactory )
线程池需要创建新的线程时,都是通过线程工厂方法来完成的。但有时需要使用定制的线程工厂方法。
public interface ThreadFactory {
Thread newThread(Runnable r);
}
/**
* 自定义线程工厂
*/
public class MyThreadFactory implements ThreadFactory {
private final String poolName;//线程池名
public MyThreadFactory(String poolName) {
this.poolName = poolName;
}
@Override
public Thread newThread(Runnable runnable) {
return new MyAppThread(runnable, poolName);
}
}
public class MyAppThread extends Thread {
public static final String DEFAULT_NAME = "MyAppThread";
private static volatile boolean debugLifecycle = false;
private static final AtomicInteger created = new AtomicInteger();
private static final AtomicInteger alive = new AtomicInteger();
private static final Logger log = Logger.getAnonymousLogger();
public MyAppThread(Runnable r) {
this(r, DEFAULT_NAME);
}
public MyAppThread(Runnable runnable, String name) {
super(runnable, name + "-" + created.incrementAndGet());
//设置日志显示级别
log.setLevel(Level.FINE);
//Set the handler invoked when this thread abruptly terminates due to an uncaught exception
setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t,
Throwable e) {
log.log(Level.SEVERE,
"UNCAUGHT in thread " + t.getName(), e);
}
});
}
public void run() {
// Copy debug flag to ensure consistent value throughout.
boolean debug = debugLifecycle;
if (debug) log.log(Level.SEVERE, "Created " + getName());
try {
alive.incrementAndGet();
super.run();
} finally {
alive.decrementAndGet();
if (debug) log.log(Level.SEVERE, "Exiting " + getName());
}
}
public static int getThreadsCreated() {
return created.get();
}
public static int getThreadsAlive() {
return alive.get();
}
public static boolean getDebug() {
return debugLifecycle;
}
public static void setDebug(boolean b) {
debugLifecycle = b;
}
}
测试:
public class TestMain {
public static void main(String[] args) {
MyThreadFactory factory = new MyThreadFactory("Wow");
MyAppThread.setDebug(true);
MyAppThread t =(MyAppThread) factory.newThread(new Runnable() {
@Override
public void run() {
int a = 1/0;//没有捕获的异常
}
});
t.start();
//*******************************************************************//
MyThreadFactory factory2 = new MyThreadFactory("hoo");
MyAppThread.setDebug(true);
MyAppThread t2 =(MyAppThread) factory2.newThread(new Runnable() {
@Override
public void run() {
int a = 1/0;//没有捕获的异常
}
});
t2.start();
}
}
测试结果:
(5)扩展ThreadPoolExecutor
可以根据需求扩展ThreadPoolExecutor重写beforeExecute、afterExecute、terminated等方法