一、自定义线程池
1. 介绍
1.1 引入原因
1. 一个任务过来,一个线程去做。如果每次过来都创建新线程,性能低且比较耗费内存
2. 线程数多于cpu核心,线程切换,要保存原来线程的状态,运行现在的线程,势必会更加耗费资源
线程数少于cpu核心,不能很好的利用多线程的性能
3. 充分利用已有线程,去处理原来的任务
1.2. 线程池组件
1. 消费者(线程池): 保存一定数量线程来处理任务
2. 生产者: 客户端源源不断产生的新任务
3. 阻塞队列(blocking queue): 平衡消费者和生产者之间任务处理的一个等待队列
- 生产任务速度较快,多余的任务要等
- 生产任务速度慢,那么线程池中存活的线程等
2. 自定义线程池
2.1. 阻塞队列
package com.nike.erick.d06;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class BlockingQueue<T> {
private int capacity;
private Deque<T> blockQueue = new ArrayDeque();
private ReentrantLock reentrantLock = new ReentrantLock(true);
private Condition producerRoom = reentrantLock.newCondition();
private Condition consumerRoom = reentrantLock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void addTask(T task) {
reentrantLock.lock();
try {
while (blockQueue.size() == capacity) {
try {
System.out.println("等待添加任务到阻塞队列:" + task);
producerRoom.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("任务添加到阻塞队列:" + task);
blockQueue.addLast(task);
consumerRoom.signalAll();
} finally {
reentrantLock.unlock();
}
}
public boolean addTask(T task, long timeout, TimeUnit timeUnit) {
long nanos = timeUnit.toNanos(timeout);
reentrantLock.lock();
try {
while (blockQueue.size() == capacity) {
try {
if (nanos < 0) {
return false;
}
nanos = producerRoom.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("添加任务到阻塞队列:" + task);
blockQueue.add(task);
consumerRoom.signalAll();
return true;
} finally {
reentrantLock.unlock();
}
}
public T getTask() {
reentrantLock.lock();
try {
while (blockQueue.isEmpty()) {
try {
consumerRoom.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T task = blockQueue.removeFirst();
producerRoom.signalAll();
return task;
} finally {
reentrantLock.unlock();
}
}
public T getTaskWithinTime(long timeout, TimeUnit timeUnit) {
long nanos = timeUnit.toNanos(timeout);
reentrantLock.lock();
try {
while (blockQueue.isEmpty()) {
try {
if (nanos < 0) {
return null;
}
nanos = consumerRoom.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T task = blockQueue.removeFirst();
producerRoom.signalAll();
return task;
} finally {
reentrantLock.unlock();
}
}
public synchronized int size() {
return blockQueue.size();
}
}
2. 线程池
package com.nike.erick.d06;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class ErickThreadPool {
private BlockingQueue blockingQueue;
private int blockingQueueCapacity;
private int coreThreadSize;
private long timeout;
private TimeUnit timeUnit;
private Set<Worker> pool = new HashSet<>();
public ErickThreadPool(int coreThreadSize, int blockingQueueCapacity) {
this.coreThreadSize = coreThreadSize;
this.blockingQueue = new BlockingQueue(blockingQueueCapacity);
}
public ErickThreadPool(int coreThreadSize, int blockingQueueCapacity, long timeout, TimeUnit timeUnit) {
this(coreThreadSize, blockingQueueCapacity);
this.timeout = timeout;
this.timeUnit = timeUnit;
}
public synchronized void execute(Runnable task, RejectHandler rejectHandler) {
if (pool.size() < coreThreadSize) {
Worker worker = new Worker(task);
System.out.println("创建新的线程来执行任务:" + task);
pool.add(worker);
worker.start();
} else {
rejectHandler.addTask(task, blockingQueue);
}
}
class Worker extends Thread {
private Runnable task;
public Worker(Runnable task) {
this.task = task;
}
@Override
public void run() {
while (task != null || (task = (Runnable) blockingQueue.getTaskWithinTime(timeout, timeUnit)) != null) {
try {
task.run();
} catch (Exception e) {
System.out.println("执行任务出错");
} finally {
task = null;
}
}
synchronized (pool) {
System.out.println("线程销毁:" + this);
pool.remove(this);
}
}
}
}
interface RejectHandler {
void addTask(Runnable task, BlockingQueue blockingQueue);
}
3. 测试
package com.nike.erick.d06;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class BlockingQueue<T> {
private int capacity;
private Deque<T> blockQueue = new ArrayDeque();
private ReentrantLock reentrantLock = new ReentrantLock(true);
private Condition producerRoom = reentrantLock.newCondition();
private Condition consumerRoom = reentrantLock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void addTask(T task) {
reentrantLock.lock();
try {
while (blockQueue.size() == capacity) {
try {
System.out.println("等待添加任务到阻塞队列:" + task);
producerRoom.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("任务添加到阻塞队列:" + task);
blockQueue.addLast(task);
consumerRoom.signalAll();
} finally {
reentrantLock.unlock();
}
}
public boolean addTask(T task, long timeout, TimeUnit timeUnit) {
long nanos = timeUnit.toNanos(timeout);
reentrantLock.lock();
try {
while (blockQueue.size() == capacity) {
try {
if (nanos < 0) {
return false;
}
nanos = producerRoom.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("添加任务到阻塞队列:" + task);
blockQueue.add(task);
consumerRoom.signalAll();
return true;
} finally {
reentrantLock.unlock();
}
}
public T getTask() {
reentrantLock.lock();
try {
while (blockQueue.isEmpty()) {
try {
consumerRoom.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T task = blockQueue.removeFirst();
producerRoom.signalAll();
return task;
} finally {
reentrantLock.unlock();
}
}
public T getTaskWithinTime(long timeout, TimeUnit timeUnit) {
long nanos = timeUnit.toNanos(timeout);
reentrantLock.lock();
try {
while (blockQueue.isEmpty()) {
try {
if (nanos < 0) {
return null;
}
nanos = consumerRoom.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T task = blockQueue.removeFirst();
producerRoom.signalAll();
return task;
} finally {
reentrantLock.unlock();
}
}
public synchronized int size() {
return blockQueue.size();
}
}
4. 获取任务超时等待
- 线程池中的线程,在执行完毕任务后,会一直尝试从阻塞队列中获取任务
- 设置超时等待,超时后,关闭线程池中的线程
3. 拒绝策略
- 生产者添加了多个任务,任务的数量超过了 线程池中线程数+阻塞队列中最大阻塞数
- 主线程就会被阻塞,一直不能往下进行
- 应该给主线程一个选择的机会,决定在这个时候,如何执行新任务的添加
- 超时未添加到阻塞队列里面,该如何做决定?拒绝策略
策略模式
1. 一直等待
2. 超时等待,过时不加
3. 让调用者自己放弃添加
4. 让调用者抛出异常
5. 让调用者自己执行任务
二、JDK线程池
1. 类图
2. 线程状态
- ThreadPoolExecutor 使用int的高3位来表示线程池状态,低29位表示线程数量
3. ThreadPoolExecutor
3.1 构造方法
int corePoolSize: 核心线程数
int maximumPoolSize: 最大线程数
long keepAliveTime: 救急线程数执行任务完后存活时间
TimeUnit unit: 救急线程数执行任务完后存活时间
BlockingQueue<Runnable> workQueue: 阻塞队列
ThreadFactory threadFactory: 线程生产工厂,为线程起名字
RejectedExecutionHandler handler: 拒绝策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
3.2 核心线程和救急线程
1. 核心线程: 执行完任务后,会继续保留在线程池中
2. 救急线程:如果阻塞队列已满,并且没有空余的核心线程。那么会创建救急线程来执行任务
2.1 任务执行完毕后,这个线程就会被销毁(临时工)
2.2 必须是有界阻塞,如果是无界队列,则不需要创建救急线程
3. 拒绝策略: 有界队列,核心线程满负荷,阻塞队列已满,无空余救急线程,才会执行拒绝
3.3 JDK拒绝策略
- 如果线程达到最大线程数,救急线程也满负荷,且有界队列也满了,JDK 提供了4种拒绝策略
AbortPolicy: 调用者抛出RejectedExecutionException, 默认策略
CallerRunsPolicy: 调用者运行任务
DiscardPolicy: 放弃本次任务
DiscardOldestPolicy: 放弃队列中最早的任务,本任务取而代之
4. Executors类工厂方法
- 默认的构造方法来创建线程池,参数过多,JDK提供了工厂方法,来创建线程池
4.1 固定大小
- 核心线程数 = 最大线程数,救急线程数为0
- 阻塞队列:无界
任务量已知,但是线程执行时间较长
执行任务后,线程并不会结束
public static ExecutorService newFixedThreadPool(int nThreads) {
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
4.2 带缓冲
- 核心线程数为0, 最大线程数为Integer的无限大
- 全部是救急线程,等待时间是60s,60s后就会消亡
- SynchronousQueue: 容量为0,只能存放一个,进来一个,必须出去后,后续的才能进来
- 整个线程池数会随着任务数目增长,1分钟后没有其他活动会消亡
1. 时间较短的线程
2. 数量大,任务执行时间长,会造成 OutOfMmeory问题
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
4.3. 单线程
- 线程池大小始终为1个,不能改变线程数
- 相比自定义一个线程来执行,线程池可以保证前面任务的失败,不会影响到后续任务
自定义线程: 执行多个任务时,一个出错,后续都能不能执行了
单线程池: 一个任务失败后,会结束出错线程。重新new一个线程来执行下面的任务
单线程池: 保证所有任务都是串行
newFixedThreadPool: 可以修改线程大小
newSingleThreadExecutor: 不可以修改
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
package com.nike.erick.d07;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo01 {
public static void main(String[] args) {
method03();
}
private static void method01() {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
}
private static void method02() {
ExecutorService pool = Executors.newCachedThreadPool();
pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
}
private static void method03() {
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.execute(() -> {
int i = 1 / 0;
System.out.println(Thread.currentThread().getName() + " running");
});
pool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " running");
});
pool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " running");
});
}
}
5. 提交任务
5.1. execute
void execute(Runnable command)
5.2. submit
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Callable<T> task);
3. invokeAll
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
4. invokeAny
package com.nike.erick.d07;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
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;
import java.util.concurrent.TimeUnit;
public class Demo02 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService pool = Executors.newFixedThreadPool(10);
method05(pool);
}
public static void method01(ExecutorService pool) {
pool.execute(() -> System.out.println(Thread.currentThread().getName() + " running"));
}
public static void method02(ExecutorService pool) throws InterruptedException {
Future<?> result = pool.submit(new Thread(() -> System.out.println(Thread.currentThread().getName() + " running")));
TimeUnit.SECONDS.sleep(1);
System.out.println(result.isDone());
System.out.println(result.isCancelled());
}
public static void method03(ExecutorService pool) throws InterruptedException, ExecutionException {
Future<String> submit = pool.submit(() -> "success");
TimeUnit.SECONDS.sleep(1);
System.out.println(submit.isDone());
System.out.println(submit.isCancelled());
System.out.println(submit.get());
}
public static void method04(ExecutorService pool) throws InterruptedException {
Collection tasks = new ArrayList();
for (int i = 0; i < 10; i++) {
int round = i;
tasks.add((Callable) () -> {
System.out.println(Thread.currentThread().getName() + " running");
return "success:" + round;
});
}
List results = pool.invokeAll(tasks);
TimeUnit.SECONDS.sleep(1);
System.out.println(results);
}
public static void method05(ExecutorService pool) throws InterruptedException, ExecutionException {
ExecutorService service = Executors.newFixedThreadPool(1);
Collection<Callable<String>> tasks = new ArrayList<>();
tasks.add(() -> {
System.out.println("first task");
TimeUnit.SECONDS.sleep(1);
return "success";
});
tasks.add(() -> {
System.out.println("second task");
TimeUnit.SECONDS.sleep(2);
return "success";
});
tasks.add(() -> {
System.out.println("third task");
TimeUnit.SECONDS.sleep(3);
return "success";
});
String result = pool.invokeAny(tasks);
System.out.println(result);
}
}
6. 关闭线程池
6.1 shutdown
- 将线程池的状态改变为SHUTDOWN状态
- 不会接受新任务,已经提交的任务不会停止
- 不会阻塞调用线程的执行
void shutdown();
package com.dreamer.multithread.day09;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Demo04 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " first running");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " second running");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " third running");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
pool.shutdown();
System.out.println("main thread ending");
}
}
6.2. shutdownNow
- 不会接受新任务
- 没执行的任务会打断
- 将等待队列中的任务返回
List<Runnable> shutdownNow();
package com.dreamer.multithread.day09;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Demo04 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " first running");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " second running");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " third running");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
List<Runnable> leftOver = pool.shutdownNow();
System.out.println(leftOver.size());
System.out.println("main thread ending");
}
}
三、线程池拓展
1. 线程数量
- 过小,导致cpu资源不能充分利用,浪费性能
- 过大,线程上下文切换,导致占用内存过多,浪费性能
1.1 CPU密集型
- 如果线程的任务主要是和cpu资源打交道,称为CPU密集型
- 线程数量: 核心数+1
- +1: 保证某线程由于某些原因(操作系统方面)导致暂停时,额外线程可以启动,不浪费CPU资源
1. 2. IO密集型
- IO操作,RPC调用,数据库访问时,CPU是空闲的,称为IO密集型
- 线程数 = 核数* 期望cpu利用率* (cpu计算时间+cpu等待时间)/cpu计算时间
2. 调度功能
2.1. 延时执行
package com.dreamer.multithread.day09;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo05 {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
pool.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " first running");
}
}, 1, TimeUnit.SECONDS);
pool.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " second running");
}
}, 5, TimeUnit.SECONDS);
}
}
package com.dreamer.multithread.day09;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo06 {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
pool.schedule(new Runnable() {
@Override
public void run() {
int i = 1 / 0;
System.out.println(Thread.currentThread().getName() + " first running");
}
}, 1, TimeUnit.SECONDS);
pool.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " second running");
}
}, 2, TimeUnit.SECONDS);
}
}
2. 定时执行
- scheduleAtFixedRate
- scheduleWithFixedDelay
package com.dreamer.multithread.day09;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo07 {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
pool.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task is running");
}
}, 3, 2, TimeUnit.SECONDS);
}
}
3. 异常处理
3.1. 不处理异常
package com.dreamer.multithread.day09;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo08 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(new Runnable() {
@Override
public void run() {
int i = 1 / 0;
System.out.println(Thread.currentThread().getName() + " task running");
}
});
}
}
2. 任务执行者自己处理
package com.dreamer.multithread.day09;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo08 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(new Runnable() {
@Override
public void run() {
try {
int i = 1 / 0;
System.out.println(Thread.currentThread().getName() + " task running");
} catch (Exception e) {
e.printStackTrace();
return;
}
}
});
}
}
3. 线程池处理
package com.dreamer.multithread.day09;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class Demo08 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService pool = Executors.newFixedThreadPool(1);
Future<?> result = pool.submit(new Runnable() {
@Override
public void run() {
int i = 1 / 0;
System.out.println(Thread.currentThread().getName() + " task running");
}
});
TimeUnit.SECONDS.sleep(1);
System.out.println(result.get());
}
}