线程池超负载了怎么办?都有哪些拒绝策略?
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExecutorServiceDemo {
static void log(String msg) {
System.out.println(System.currentTimeMillis() + " -> " + msg);
}
public static void main(String[] args) throws Exception {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1));
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 0; i < 10; i++) {
final int index = i;
pool.submit(new Runnable() {
public void run() {
log("run task:" + index + " -> " + Thread.currentThread().getName());
try {
Thread.sleep(1000L);
} catch (Exception e) {
e.printStackTrace();
}
log("run over:" + index + " -> " + Thread.currentThread().getName());
}
});
}
log("before sleep");
Thread.sleep(4000L);
log("before shutdown()");
pool.shutdown();
log("after shutdown(),pool.isTerminated=" + pool.isTerminated());
pool.awaitTermination(1, TimeUnit.SECONDS);
log("now,pool.isTerminated=" + pool.isTerminated());
}
}
程序设置的线程池只有一个线程,并且允许最大的线程数量也为1,同时任务队列的最大界限为1。
handler.rejectedExecution(command, this);
}
自己定义线程创建:ThreadFactory
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 自定义线程池 :public interface ThreadFactory
* 根据需要创建新线程的对象
* Thread newThread(Runnable r)
* @author Administrator
*
*/
public class MyThreadFactory {
public static class MyTask implements Runnable {
@Override
public void run() {
System.out.println( System.currentTimeMillis() + " Thread id:" + Thread.currentThread().getId());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
MyTask task = new MyTask();
ExecutorService exec = new ThreadPoolExecutor(5,5,0L,TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactory(){
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r,"后台线程");
t.setDaemon(true);
System.out.println("create " + t.getName()+ " " + t.getId());
return t;
}
});
for(int i = 0; i < 5; i++) {
exec.submit(task);
}
TimeUnit.SECONDS.sleep(2);
}
}
对线程池的扩展
protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
同时再看
ThreadPoolExecutor中有一个这样的类, private final class Worker extends AbstractQueuedSynchronizer implements Runnable它里面有一个方法runWorker
final void runWorker(Worker w) {
Runnable task = w.firstTask;
w.firstTask = null;
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
clearInterruptsForTaskRun();
try {
beforeExecute(w.thread, task);<span style="white-space:pre"> </span>//运行前
Throwable thrown = null;
try {
task.run();<span style="white-space:pre"> </span>//运行任务
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);<span style="white-space:pre"> </span>//运行结束后
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
ThreadPoolExecutor中工作线程正是Worker的实例(它是把Runnable对象进行了包装),Worker.runWorker()会被线程池以多线程模式异步调用,即它会被多个线程访问,因此其beforeExecute()它们也会被多线程同时访问。
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExtThreadPool {
public static class MyTask implements Runnable {
private String name;
public MyTask(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("正在执行线程id:" + Thread.currentThread().getId() + " Task name " + name);
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec =
new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>())
//直接获得ThreadPoolExecutor的子类,并且重写protect钩子方法
{
@Override
protected synchronized void beforeExecute(Thread t, Runnable r) {
System.out.print(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS").format(new Date()));
System.out.println("准备执行:" + ((MyTask)r).name);
}
@Override
protected synchronized void afterExecute(Runnable r, Throwable t) {
System.out.print(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS").format(new Date()));
System.out.println("执行完成:" + ((MyTask)r).name);
}
@Override
protected synchronized void terminated() {
System.out.print(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS").format(new Date()));
System.out.println("线程池退出...");
}
};
for(int i = 0; i < 5; i++) {
MyTask task = new MyTask("Task" + i);
exec.execute(task);
Thread.sleep(10);
}
exec.shutdown();
}
}
这里需要注意打印时间这句会发生线程安全。用Synchronized监视器让线程在临界区进行互斥的执行。
在线程池中寻找堆栈异常信息
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 演示线程池中的异常
* @author Administrator
*
*/
public class DivTask implements Runnable {
int a,b;
public DivTask(int a, int b) {
super();
this.a = a;
this.b = b;
}
@Override
public void run() {
double res = a/b;
System.out.println(res);
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++) {
es.execute(new DivTask(100, i));
}
es.shutdown();
}
}
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at ch3.DivTask.run(DivTask.java:25)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
50.0
33.0
25.0
100.0
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TraceThreadPoolExecutor extends ThreadPoolExecutor {
public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void execute(Runnable command) {
super.execute(wrap(command,clientStack(),Thread.currentThread().getName()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(wrap(task,clientStack(),Thread.currentThread().getName()));
}
public Exception clientStack() {
return new Exception("Client stack trace");
}
private Runnable wrap(final Runnable task,final Exception clientStack,String clientThreadName) {
return new Runnable() {
@Override
public void run() {
try {
task.run();
} catch(Exception e) {
clientStack.printStackTrace();
throw e;
}
}
};
}
}
100.0
25.0
33.0
50.0
java.lang.Exception: Client stack trace
at DivTask$TraceThreadPoolExecutor.clientStack(DivTask.java:53)
at DivTask$TraceThreadPoolExecutor.execute(DivTask.java:46)
at DivTask.main(DivTask.java:75)
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at DivTask.run(DivTask.java:32)
at DivTask$TraceThreadPoolExecutor$1.run(DivTask.java:61)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Runnable
,两者都是为那些其实例可能被另一个线程执行的类设计的。但是Runnable 不会返回结果,并且无法抛出经过检查的异常。
三个区别:
1、接收的参数不一样
2、submit有返回值,而execute没有
Method submit extends base method Executor.execute by creating and returning a Future that can be used to cancel execution and/or wait for completion.
用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。然后我就可以把所有失败的原因综合起来发给调用者。
个人觉得cancel execution这个用处不大,很少有需要去取消执行的。
而最大的用处应该是第二点。
3、submit方便Exception处理
There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute
this exception will go to the uncaught exception handler (when you don't have provided one explicitly, the default one will just print the stack trace to System.err
). If you submitted the task with submit
any thrown exception, checked or not, is then part of the task's return status. For a task that was submitted with submit
and that terminates with an exception, the Future.get
will rethrow this exception, wrapped in an ExecutionException
.
意思就是如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。
比如说,我有很多更新各种数据的task,我希望如果其中一个task失败,其它的task就不需要执行了。那我就需要catch Future.get抛出的异常,然后终止其它task的执行,代码如下:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ExecutorServiceTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> resultList = new ArrayList<Future<String>>();
// 创建10个任务并执行
for (int i = 0; i < 10; i++) {
// 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
Future<String> future = executorService.submit(new TaskWithResult(i));
// 将任务执行结果存储到List中
resultList.add(future);
}
executorService.shutdown();
// 遍历任务的结果
for (Future<String> fs : resultList) {
try {
System.out.println(fs.get()); // 打印各个线程(任务)执行的结果
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
executorService.shutdownNow();
e.printStackTrace();
return;
}
}
}
}
class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
/**
* 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。
*
* @return
* @throws Exception
*/
public String call() throws Exception {
System.out.println("call()方法被自动调用,干活!!! " + Thread.currentThread().getName());
if (new Random().nextBoolean())
throw new TaskException("Meet error in task." + Thread.currentThread().getName());
// 一个模拟耗时的操作
for (int i = 999999999; i > 0; i--)
;
return "call()方法被自动调用,任务的结果是:" + id + " " + Thread.currentThread().getName();
}
}
class TaskException extends Exception {
public TaskException(String message) {
super(message);
}
}
上面的代码解释的也比较详细了。。。。运行的结果和我们在上面自己扩展的一样都是完整的栈信息。
call()方法被自动调用,干活!!! pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-3
call()方法被自动调用,干活!!! pool-1-thread-1
call()方法被自动调用,干活!!! pool-1-thread-6
call()方法被自动调用,干活!!! pool-1-thread-4
call()方法被自动调用,干活!!! pool-1-thread-5
call()方法被自动调用,干活!!! pool-1-thread-9
call()方法被自动调用,干活!!! pool-1-thread-10
call()方法被自动调用,干活!!! pool-1-thread-8
call()方法被自动调用,干活!!! pool-1-thread-7
java.util.concurrent.ExecutionException: threadTest.TaskException: Meet error in task.pool-1-thread-1
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at threadTest.ExecutorServiceTest.main(ExecutorServiceTest.java:29)
Caused by: threadTest.TaskException: Meet error in task.pool-1-thread-1
at threadTest.TaskWithResult.call(ExecutorServiceTest.java:57)
at threadTest.TaskWithResult.call(ExecutorServiceTest.java:41)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)