由于之前说使用Exectors类创建线程池时候,会造成OOM,建议手动创建线程池。
今天就尝试手动创建线程池,并且介绍它的4中拒绝策略和手动写拒绝策略。
在创建线程池之前,我们先看看线程池创建的构造函数和各个字段的意义。
corePoolSize:核心线程数,当线程数未达到核心线程时,新的任务会创建新的线程,即使有线程空闲也会创建新的线程。
maximumPoolSize:线程最大数,当阻塞队列满了之后,就会创建新的线程,达到最大线程数为止。
keepAliveTime:线程空闲时间,当线程空闲时间超过此值,并且线程数大于核心线程,此线程将被回收,以此来保证核心线程数量。
unit:线程空余时间单位。
workQueue:阻塞队列,当任务大于线程数,阻塞队列还未满,就会加入阻塞队列中
threadFactory:管理创建的线程,可以设置线程的名称等属性。
handler:拒绝策略,当阻塞对面已满,线程达到最大线程,就会采用阻塞策略。
知道了各个参数的意义,现在来手动创建线程池。
先创建一个任务,再创建一个线程池。
package test.thread.pool;
public class Task implements Runnable {
private int i;
public Task(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " print " + i);
}
}
package test.thread.pool;
import java.util.concurrent.*;
public class CreateThreadPool {
public static void main(String[] args) {
BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,
60, TimeUnit.SECONDS, blockingQueue,
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 10; i++) {
Task task = new Task(i);
executor.execute(task);
}
executor.shutdown();
}
}
我们创建了一个核心线程数为2个,最大线程数为4,线程空闲时间为60s,阻塞队列大小为2的线程池,阻塞策略为默认的
AbortPolicy 根据源码可以看到,此种拒绝策略直接抛出rejectedExecution异常,执行结果如下。
pool-1-thread-1 print 0
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task test.thread.pool.Task@6d6f6e28 rejected from java.util.concurrent.ThreadPoolExecutor@135fbaa4[Running, pool size = 4, active threads = 3, queued tasks = 0, completed tasks = 2]
pool-1-thread-2 print 1
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
pool-1-thread-4 print 5
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
pool-1-thread-1 print 2
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
pool-1-thread-3 print 4
at test.thread.pool.CreateThreadPool.main(CreateThreadPool.java:15)
pool-1-thread-4 print 3
DiscardPolicy 拒绝策略,这种策略是不会做任何处理,不会抛出异常,直接丢弃任务,运行结果如下
import java.util.concurrent.*;
public class CreateThreadPool {
public static void main(String[] args) {
BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,
60, TimeUnit.SECONDS, blockingQueue,
Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 10; i++) {
Task task = new Task(i);
executor.execute(task);
}
executor.shutdown();
}
}
运行结果如下,可以看到没有抛出异常,不做任何处理,直接丢弃了后面的任务。
pool-1-thread-2 print 1
pool-1-thread-3 print 4
pool-1-thread-2 print 2
pool-1-thread-4 print 5
pool-1-thread-1 print 0
pool-1-thread-3 print 3
Process finished with exit code 0
DiscardOldestPolicy 拒绝策略是从队列中移除老的任务,继续执行任务。
由运行结果可以看到,前面2,3,6,7任务被丢弃了。
pool-1-thread-1 print 0
pool-1-thread-2 print 1
pool-1-thread-2 print 9
pool-1-thread-1 print 8
pool-1-thread-3 print 4
pool-1-thread-4 print 5
Process finished with exit code 0
CallerRunsPolicy 是不会丢弃任务,直接运行任务的run方法,即使用主线程执行任务,执行结果如下。
根据运行结果可以看到,其中有main线程执行任务,因为当线程不足时,会由主线程直接执行run方法。
main print 6
pool-1-thread-4 print 5
pool-1-thread-2 print 1
pool-1-thread-2 print 3
pool-1-thread-1 print 0
pool-1-thread-3 print 4
pool-1-thread-4 print 2
main print 7
pool-1-thread-2 print 8
pool-1-thread-1 print 9
Process finished with exit code 0
以上就是线程池的4种拒绝策略,但是并不是很好,还是建议大家手动创建拒绝策略,下面给大家提供一种拒绝策略,供大家参考,当线程拒绝时,还是重复向队列中加,加进去继续让线程处理,执行结果如下。
package test.thread.pool;
import java.util.concurrent.*;
public class CreateThreadPool {
public static void main(String[] args) {
BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,
60, TimeUnit.SECONDS, blockingQueue,
Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
for (int i = 0; i < 10; i++) {
Task task = new Task(i);
executor.execute(task);
}
executor.shutdown();
}
}
运行结果可以看到,任务都被执行,并且没有使用主线程运行。
pool-1-thread-1 print 0
pool-1-thread-4 print 5
pool-1-thread-3 print 4
pool-1-thread-2 print 1
pool-1-thread-1 print 2
pool-1-thread-4 print 3
pool-1-thread-1 print 8
pool-1-thread-3 print 6
pool-1-thread-2 print 7
pool-1-thread-4 print 9
Process finished with exit code 0
最后再贴上更改线程名称等属性的代码
package test.thread.pool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class CreateThreadPool {
static class NameThreadFactory implements ThreadFactory {
private AtomicInteger count = new AtomicInteger();
private String name;
public NameThreadFactory(String name) {
this.name = name + "-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, name + count.incrementAndGet());
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
public static void main(String[] args) {
BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,
60, TimeUnit.SECONDS, blockingQueue,
new CreateThreadPool.NameThreadFactory("test-thread-pool"), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
for (int i = 0; i < 10; i++) {
Task task = new Task(i);
executor.execute(task);
}
executor.shutdown();
}
}
运行结果如下,可以看到线程名称变化
test-thread-pool-1 print 0
test-thread-pool-2 print 1
test-thread-pool-2 print 3
test-thread-pool-1 print 2
test-thread-pool-2 print 6
test-thread-pool-1 print 7
test-thread-pool-2 print 8
test-thread-pool-1 print 9
test-thread-pool-3 print 4
test-thread-pool-4 print 5
Process finished with exit code 0
以上就是手动创建线程,以及创建线程策略和线程属性的例子,如有不对的地方,还请指出,谢谢