1.Executor
jdk提供了一套Executor框架,本质上是一个线程池。
- newFixedThreadPool()方法:该方法返回一个固定数量的线程池。该线程池中的线程数量始终不变,当有一个新任务提交时,线程池中若有空闲线程,则立即执行,若没有,则任务会暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
- newSingleThreadExecutor():该方法返回一个只有一个线程的线程池。若有多余一个任务被提交到该线程池,任务会被保存到一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
- newCachedThreadPool():该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在使用完毕后,将返回线程池进行复用。
- newSingleThreadScheduledExecutor():该方法返回一个ScheduledExecutorService对象,线程池大小为1。ScheduledExecutorService接口在ExecutorService接口之上扩展了在给定时间执行某任务的功能,如在某个固定的延时之后执行,或者周期性的执行某个任务。
- newScheduledThreadPool():该方法也返回一个ScheduledExecutorService对象,但该线程池可以指定线程数量。
public class Test {
public static class MyTask implements Runnable {
@Override
public void run() {
System.out.println(System.currentTimeMillis() + ":Thread ID:" + Thread.currentThread().getId());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyTask myTask = new MyTask();
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
es.submit(myTask);
}
}
}
2.拒绝策略
当任务数量超过系统实际承载能力时,用拒绝策略进行补救。
- AbortPolicy():该策略会直接抛出异常,阻止系统正常工作。
- CallerRunsPolicy():只要线程池未关闭,该策略直接在调用者的线程中,运行当前被丢弃的任务。
- DiscardOldestPolicy():该策略将丢弃最老的一个请求,也就是即将被执行的任务,并尝试在次提交当前任务.
- DiscardPolicy():该策略默默地丢弃无法处理的任务,不予任何处理。
public class Test {
public static class MyTask implements Runnable {
@Override
public void run() {
System.out.println(System.currentTimeMillis() + ":Thread ID:" + Thread.currentThread().getId());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException{
MyTask myTask = new MyTask();
ExecutorService es = new ThreadPoolExecutor(5, 5, 0L,
TimeUnit.MICROSECONDS, new LinkedBlockingDeque<Runnable>(10), Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r.toString()+" is discard");
}
});
for (int i = 0; i < Integer.MAX_VALUE; i++) {
es.submit(myTask);
Thread.sleep(10);
}
}
}
在执行几个任务后,拒绝策略就开始生效了。在实际应用中,可以利用这一点来分析系统的负载和任务丢失情况。
3.扩展线程池
ThreadPoolExecutor是一个可扩展的线程池。
public class Test {
public static class MyTask implements Runnable {
public String name;
public MyTask(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("正在执行" + ":Thread ID:" + Thread.currentThread().getId() + ",Task name = " + name);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService es = new ThreadPoolExecutor(5, 5, 0L,
TimeUnit.MICROSECONDS, new LinkedBlockingDeque<Runnable>()) {
@Override
protected void beforeExecute(Thread t, Runnable r) {
System.out.println("准备执行:" + ((MyTask) r).name);
}
@Override
protected void afterExecute(Runnable r,Throwable t){
System.out.println("执行完成:"+((MyTask) r).name);
}
@Override
protected void terminated(){
System.out.println("线程退出");
}
};
for (int i = 0; i < 5; i++) {
MyTask myTask = new MyTask("TASK-GEYM-"+i);
es.execute(myTask);
Thread.sleep(10);
}
es.shutdown();
}
}
所有任务的前中后时间点以及任务名字都可以捕捉。便于应用程序调试。
4.在线程池中捕获异常
改造ThreadPoolExecutor方法,使其能抛出异常,以便于查询错误的地方。
public class Test extends ThreadPoolExecutor {
public Test(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void execute(Runnable task) {
super.execute(wrap(task, clientTrace(), Thread.currentThread().getName()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(wrap(task, clientTrace(), Thread.currentThread().getName()));
}
private Exception clientTrace() {
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) {
e.printStackTrace();
throw e;
}
}
};
}
public static void main(String[] args) {
ThreadPoolExecutor pools = new Test(0, Integer.MAX_VALUE, 0L, TimeUnit.SECONDS,
new SynchronousQueue<>());
for (int i = 0; i < 5; i++) {
pools.execute(new DivTask(100,i));
}
}
private static class DivTask implements Runnable {
int a, b;
public DivTask(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
double re = a/b;
System.out.println(re);
}
}
}
5.Fork/Join框架
假如要处理1000个数据,但不具备同时处理的能力,先处理10个,在分阶段处理100次,最后将结果合并。某一线程处理完后可以帮助其他未完成的线程处理。
public class Test extends RecursiveTask<Long> {
private static final int THRESHOLD = 10000;
private long start;
private long end;
public Test(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long sum = 0;
boolean canConpute = (end - start) < THRESHOLD;
if (canConpute) {
for (long i = start; i < end; i++) {
sum += i;
}
}else {
// 分成100个任务
long step = (start+end)/100;
ArrayList<Test> subTasks = new ArrayList<>();
long pos = start;
for (int i = 0; i < 100; i++) {
long lastOne = pos+step;
if (lastOne>end){
lastOne = end;
}
Test subTask = new Test(pos,lastOne);
pos+=step+1;
subTasks.add(subTask);
subTask.fork();
}
for (Test t : subTasks){
sum+=t.join();
}
}
return sum;
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
Test test = new Test(0,200000L);
ForkJoinTask<Long> result = forkJoinPool.submit(test);
try {
long res = result.get();
System.out.println("sum = "+res);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}