线程池
自定义线程池的工作流程:
- 线程池刚开始没有线程时,当一个任务提交给线程池后,线程池会创建出一个新的线程来执行任务
- 当线程数达到coreSize时还没有线程空闲,这时再加入的任务就被放进队列中等待被空闲的线程执行
- 线程池的目的就是让一个线程执行多个任务,达到线程的重复利用
步骤1:自定义任务队列
public class BlockedQueue<T> {
//队列的容量
private int capacity;
//用来存放任务
private Deque<T> taskQueue = new ArrayDeque<>();
private ReentrantLock lock = new ReentrantLock();
private Condition fullCondition = lock.newCondition();
private Condition emptyCondition = lock.newCondition();
public BlockedQueue(int capacity) {
this.capacity = capacity;
}
//从任务队列中获取任务
public T take() {
lock.lock();
try {
while (taskQueue.isEmpty()) {
try {
emptyCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = taskQueue.removeFirst();
fullCondition.signalAll(); //只会唤醒所有的因为队列已满而陷入waiting的线程
return t;
} finally {
lock.unlock();
}
}
//将任务加入到队列中
public void put(T t) {
lock.lock();
try {
while (taskQueue.size() == capacity) {
try {
fullCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
taskQueue.push(t);
emptyCondition.signalAll();
} finally {
lock.unlock();
}
}
}
步骤2:自定义线程池
class ThreadPool{
private int coreSize; //核心线程数
private BlockedQueue<Runnable> taskQueue; //存储任务
private Set<Thread> threads = new HashSet<>(); //存储线程
public ThreadPool(int coreSize,int capacity) {
this.coreSize = coreSize;
taskQueue = new BlockedQueue<>(capacity);
}
public void execute(Runnable task){
synchronized (taskQueue) {
if(threads.size() < coreSize){ //小于核心线程数时就创建新的线程并执行任务
Worker worker = new Worker(task);
worker.start();
threads.add(worker);
}else{ //当大于核心线程数后不会创建新的线程,会把任务加入到任务队列,等待这些核心线程去轮流执行
System.out.println("将任务加入队列中,此时线程量为:"+threads.size());
taskQueue.put(task);
}
}
}
//将线程进行包装能够从任务队列中获取任务并执行
class Worker extends Thread{
private Runnable task;
public Worker(Runnable task) {
this.task = task;
}
@Override
public void run() {
while (task != null || (task = taskQueue.take()) != null){ //当执行完构造方法传来的任务后就从任务队列中不断的获取
try {
task.run();
} catch (Exception e){//当任务发生异常时捕获,保证线程可以正常执行
e.printStackTrace();
}finally {
task = null;
}
//当循环结束后说明队列中的任务都已执行完毕,将当前线程this移出集合
synchronized (taskQueue){
threads.remove(this);
}
}
}
}
}
测试
public static void main(String[] args) throws InterruptedException {
ThreadPool threadPool = new ThreadPool(3,5);
for(int i = 0; i < 5; i++){
int j = i;
threadPool.execute(()->{
System.out.println("线程"+Thread.currentThread()+"执行了任务"+j);
});
}
}
对线程池进行升级,加入拒绝策略。
步骤一:加入决绝策略的接口
@FunctionalInterface
interface RejectPolicy<T>{
void reject(BlockedQueue<T> queue,T task);
}
步骤二:给任务队列增加新的具有限时功能方法,poll()和offer()。
在线程池中使用拒绝策略,当任务队列已满时按照用户自定义的方式处理任务。
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class BlockedQueue<T> {
//队列的容量
private int capacity;
//用来存放任务
private Deque<T> taskQueue = new ArrayDeque<>();
private ReentrantLock lock = new ReentrantLock();
private Condition fullCondition = lock.newCondition();
private Condition emptyCondition = lock.newCondition();
public BlockedQueue(int capacity) {
this.capacity = capacity;
}
public T take() {
lock.lock();
try {
while (taskQueue.isEmpty()) {
try {
emptyCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = taskQueue.removeFirst();
fullCondition.signalAll();
return t;
} finally {
lock.unlock();
}
}
//加入超时获取,超时后返回null
public T poll(long timeout, TimeUnit timeUnit){
lock.lock();
try {
long nanos = timeUnit.toNanos(timeout);
while (taskQueue.isEmpty()) {
try {
if(nanos < 0){
return null;
}
nanos = emptyCondition.awaitNanos(nanos); //返回剩余时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = taskQueue.removeFirst();
fullCondition.signalAll();
return t;
} finally {
lock.unlock();
}
}
public void put(T t) {
lock.lock();
try {
while (taskQueue.size() == capacity) {
try {
fullCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
taskQueue.push(t);
emptyCondition.signalAll();
} finally {
lock.unlock();
}
}
//再给定时间内将任务添加到队列中,如果失败返回false。
public boolean offer(T t,long timeout,TimeUnit timeUnit){
lock.lock();
try {
long nanos= timeUnit.toNanos(timeout);
while (taskQueue.size() == capacity) {
try {
if(nanos < 0) {
return false;
}
nanos = fullCondition.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
taskQueue.push(t);
emptyCondition.signalAll();
return true;
} finally {
lock.unlock();
}
}
//当任务数大于队列的容量时根据RejectPloicy接口规定的方式处理任务
public void tryput(RejectPolicy rejectPolicy,T task){
lock.lock();
try {
if(taskQueue.size() == capacity) {
rejectPolicy.reject(this, task);
}else{
taskQueue.addLast(task);
emptyCondition.signalAll();
}
} finally {
lock.unlock();
}
}
}
class ThreadPool{
private int coreSize; //核心线程数
private BlockedQueue<Runnable> taskQueue; //存储任务
private Set<Thread> threads = new HashSet<>(); //存储线程
private long timeout;
private TimeUnit timeUnit;
private RejectPolicy<Runnable> rejectPolicy;
public ThreadPool(int coreSize,int capacity,long timeout,TimeUnit timeUnit,RejectPolicy<Runnable> rejectPolicy) {
this.coreSize = coreSize;
taskQueue = new BlockedQueue<>(capacity);
this.timeout = timeout;
this.timeUnit = timeUnit;
this.rejectPolicy = rejectPolicy;
}
public void execute(Runnable task){
synchronized (taskQueue) {
if(threads.size() < coreSize){
Worker worker = new Worker(task);
worker.start();
threads.add(worker);
}else{
System.out.println("将任务加入队列中,此时线程量为:"+threads.size());
taskQueue.tryput(rejectPolicy,task);
}
}
}
class Worker extends Thread{
private Runnable task;
public Worker(Runnable task) {
this.task = task;
}
@Override
public void run() {
while (task != null || (task = taskQueue.poll(timeout,timeUnit)) != null){
try {
task.run();
} catch (Exception e){行
e.printStackTrace();
}finally {
task = null;
}
synchronized (taskQueue){
threads.remove(this);
}
}
}
}
}
测试
public static void main(String[] args) throws InterruptedException {
ThreadPool threadPool = new ThreadPool(3,5,1000,TimeUnit.MILLISECONDS,(queue,task)->{
task.run(); //当任务队列已满时主线程自己调用该任务
});
for(int i = 0; i < 10; i++){
int j = i;
threadPool.execute(()->{
System.out.println("线程"+Thread.currentThread()+"执行了任务"+j);
});
}
}
ThreadPoolExecutor
1)线程状态
原子变量ctl的高3位表示线程状态,低29位表示数量。可以用一次cas操作赋值
2)构造方法
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数:核心数+救急线程数
long keepAliveTime, //救急线程存活时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue, //阻塞队列:存储任务
RejectedExecutionHandler handler //拒绝策略
)
- 线程池刚开始没有线程时,当一个任务提交给线程池后,线程池会创建出一个新的线程来执行任务
- 当线程数达到coreSize时还没有线程空闲,这时再加入新的任务就被加入我认可Queue队列排队,直到被空闲的线程执行
- 如果选择有界队列,当任务数超过队列大小时会创建maximumPoolSize-corePoolSize个线程来救急
- 如果线程数达到maximunPoolSize仍然有新任务这时会采用决绝策略,jdk共提供了4种实现
- 当救急线程在高峰期过去后空闲keepAliveTime时间后就会结束
3)newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
- 核心线程数 == 最大线程数,表示不会创建救急线程,因此 keepAliveTime是0。
- 使用无界队列,可以存放任意数量的任务。
- 适合任务量已知,相对耗时的任务。
4)newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 核心线程数是0,所有的线程都是救急线程,最大数量为
Integer.MAX_VALUE
,存活时间是60‘s, - 队列采用了
SynchronousQueue
,它没有容量,只有一个线程来取得时候另一个线程才能放任务 - 适合任务数密集,但每个任务执行时间短得情况
5)newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 只有一个核心线程,当任务数多余1时会放进队列中排队等待让这一个线程执行。
- 如果自己创建一个单线程串行执行任务,线程发生异常而终止那么其他任务会无法被执行。而线程池还会新建一个线程保证正常工作。
线程池中的线程都是非守护线程,不会随着主线程结束而结束
异步模式之工作线程
有限的线程轮流执行无限多的任务,典型实现是线程池,是享元模式的体现。
不同任务类型应该使用不同的线程池,可以避免饥饿,提升效率。
1、饥饿的例子
- 两个员工是同一线程池中两个线程
- 他们处理的任务是:
- 客人点餐:必须先点完餐才能上菜
- 后厨上菜:上菜完毕后整个任务才算结束
- 如果只有两个线程,当两个线程分别去处理客人A和客人B的点餐,那么上菜没有线程去处理,两个任务都无法结束,饥饿。
public class S1HungryLock {
static final List<String> cookies = Arrays.asList("地三鲜", "黄焖鸡", "牛肉面", "汉堡");
static Random random = new Random(cookies.size());
static String cooking() {
return cookies.get(random.nextInt(cookies.size()));
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
//第一位客人
executorService.execute(() -> {
System.out.println("给客人A处理点餐");
Future<String> f = executorService.submit(() -> {
System.out.println("给客人A做菜");
return cooking();
});
try {
System.out.println("给客人A上菜:" + f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
//第二位客人
executorService.execute(() -> {
System.out.println("给客人B处理点餐");
Future<String> f = executorService.submit(() -> {
System.out.println("给客人B做菜");
return cooking();
});
try {
System.out.println("给客人B上菜:" + f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
}
}
两个任务都无法结束,解决方法是不同的线程池处理不同的任务
ExecutorService executorService1 = Executors.newFixedThreadPool(1);
ExecutorService executorService2 = Executors.newFixedThreadPool(1);
//第一位工人
executorService1.execute(() -> {
System.out.println("给客人A处理点餐");
Future<String> f = executorService2.submit(() -> {
System.out.println("给客人A做菜");
return cooking();
});
try {
System.out.println("给客人A上菜:" + f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
executorService1.execute(() -> {
System.out.println("给客人B处理点餐");
Future<String> f = executorService2.submit(() -> {
System.out.println("给客人B做菜");
return cooking();
});
try {
System.out.println("给客人B上菜:" + f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
任务调度线程池
Timer只有一个线程串行执行任务,当线程发生异常时(i/0)会导致其他任务无法执行,可以使用ScheduleExecutorService。
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2); //使用两个线程执行定时任务
//1秒后执行任务一
scheduledExecutorService.schedule(()->{
System.out.println("任务一,执行时间:"+new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},1000, TimeUnit.MILLISECONDS);
//2秒后执行任务二
scheduledExecutorService.schedule(()->{
System.out.println("任务二,执行时间:"+new Date());
},2000, TimeUnit.MILLISECONDS);
ForkJoinPool线程池
是JDK1.7加入的线程池实现,体现出分治的思想,适用于进行任务拆分的cpu密集型运算,把每个任务的分解和合并交给不同的线程来完成,提升运算效率。会默认创建与cpu核心数大小相同的线程池。
@Slf4j(topic = "c.add")
class AddTask extends RecursiveTask<Integer>{
private int n;
public AddTask(int n){
this.n = n;
}
@Override
public String toString() {
return "{"+n+"}";
}
@Override
protected Integer compute() {
if(n == 1) {
log.debug("join(),{}",n);
return 1;
}
//拆封结果
AddTask t = new AddTask(n-1);
t.fork();
log.debug("fork() {}+{}",n,t);
//合并结果
int result = n + t.join();
log.debug("join() {}+{}={}",n,t,result);
return result;
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new AddTask(5));
}
}