该栏目讲叙多线程基础、共享模型的内存、管程、无锁、不可变设计和多线程并发工具
文章目录
自定义线程池
1、背景
- :线程是一种系统资源,每创建一个线程都要占用一定的内存。在高并发的情况下,如果为每个任务都创建一个新的线程,对内存的占用是相当大的;从 CPU 的角度出发,CPU 的数量有限,如果线程数越多,那么没有获取到 CPU 时间片的线程会被阻塞,引发线程上下文切换,从而导致系统性能。所以创建一批线程,让线程得到充分的利用,这样减少内存的占用,也可以减少线程的数量,避免它们频繁的发生上下文切换
2、图解
3、实现
拒绝策略接口
@FunctionalInterface
public interface RejectPolicy<T> {
void reject(BlockingQueue<T> queue, T task);
}
阻塞队列
/**
* 阻塞队列
*/
public class BlockingQueue<T> {
/**
* 任务容量
*/
private final int capacity;
/**
* 任务队列
*/
private final Deque<T> queue = new ArrayDeque<>();
/**
* 锁
*/
private final ReentrantLock lock = new ReentrantLock();
/**
* 消费者条件
*/
Condition emptyWaitSet = lock.newCondition();
/**
* 生产者条件
*/
Condition fullWaitSet = lock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
/**
* 获取任务
*
* @return 任务
*/
public T take() {
lock.lock();
try {
while (queue.isEmpty()) {
try {
System.out.println("任务队列串没有任务了...");
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
final T task = queue.removeFirst();
System.out.println("获取的任务:" + task);
emptyWaitSet.signal();
return task;
} finally {
lock.unlock();
}
}
/**
* 获取任务,带超时
*
* @param timeout 超时时间
* @param timeUnit 超时单位
* @return 任务
*/
public T take(long timeout, TimeUnit timeUnit) {
lock.lock();
try {
long nanos = timeUnit.toNanos(timeout);
while (queue.isEmpty()) {
try {
if (nanos <= 0) {
System.out.println("获取任务超时...");
return null;
}
System.out.println("任务队列串没有任务了...");
nanos = emptyWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
final T task = queue.removeFirst();
System.out.println("获取的任务:" + task);
emptyWaitSet.signal();
return task;
} finally {
lock.unlock();
}
}
/**
* 添加任务
*
* @param task 任务
*/
public void put(T task) {
lock.lock();
try {
while (queue.size() == capacity) {
try {
System.out.println("任务队列已经存储满...");
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("任务加入队列:" + task);
queue.addLast(task);
emptyWaitSet.signal();
} finally {
lock.unlock();
}
}
/**
* 添加任务,带超时
*
* @param task 任务
* @param timeout 超时时间
* @param timeUnit 超时单位
*/
public void put(T task, long timeout, TimeUnit timeUnit) {
lock.lock();
try {
long nanos = timeUnit.toNanos(timeout);
while (queue.size() == capacity) {
try {
System.out.println("任务队列已经存储满...");
nanos = fullWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("任务加入队列:" + task);
queue.addLast(task);
emptyWaitSet.signal();
} finally {
lock.unlock();
}
}
/**
* 策略添加任务
*
* @param task 任务
* @param policy 策略
*/
public void put(T task, RejectPolicy<T> policy) {
lock.lock();
try {
if (queue.size() == capacity) {
policy.reject(this, task);
} else {
System.out.println("任务加入队列:" + task);
queue.addLast(task);
emptyWaitSet.signal();
}
} finally {
lock.unlock();
}
}
/**
* 获取队列大小
*
* @return 队列大小
*/
public int getSize() {
lock.lock();
try {
return queue.size();
} finally {
lock.unlock();
}
}
}
线程池
/**
* 线程池
*/
public class ThreadPool {
/**
* 线程核心数
*/
private final int coreSize;
/**
* 任务队列
*/
private final BlockingQueue<Runnable> taskQueue;
/**
* 线程集合
*/
private final Set<Worker> workers = new HashSet<>();
/**
* 超时时长
*/
private final long timeout;
/**
* 时间单位
*/
private final TimeUnit timeUnit;
/**
* 拒绝策略
*/
private final RejectPolicy<Runnable> policy;
public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit, int queueCapacity,
RejectPolicy<Runnable> policy) {
this.coreSize = coreSize;
this.timeout = timeout;
this.timeUnit = timeUnit;
this.taskQueue = new BlockingQueue<>(queueCapacity);
this.policy = policy;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(Runnable task) {
synchronized (workers) {
if (workers.size() < coreSize) {
Worker worker = new Worker(task);
System.out.println("添加work:" + worker + ",task=" + task);
workers.add(worker);
worker.start();
} else {
// taskQueue.put(task);
taskQueue.put(task, policy);
}
}
}
/**
* 工作线程
*/
class Worker extends Thread {
private Runnable task;
public Worker(Runnable task) {
this.task = task;
}
@Override
public void run() {
while (task != null || (task = taskQueue.take(timeout, timeUnit)) != null) {
try {
System.out.println("正在执行...task=" + task);
task.run();
} catch (Exception e) {
e.printStackTrace();
} finally {
task = null;
}
}
synchronized (workers) {
System.out.println("worker被移除,worker=" + this);
workers.remove(this);
}
}
}
}
测试类
/**
* 测试类
*/
public class TestDemo {
public static void main(String[] args) {
ThreadPool pool = new ThreadPool(2, 1000, TimeUnit.MILLISECONDS, 5, (queue, task) -> {
// 一直等待
queue.put(task);
// 超时等待
queue.put(task, 500, TimeUnit.MILLISECONDS);
// 让调用者放弃任务
System.out.println("放弃task=" + task);
// 自己执行任务
task.run();
// 抛出异常
throw new RuntimeException("任务执行失败,task=" + task);
});
for (int i = 0; i < 10; i++) {
int index = i;
pool.execute(() -> {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行任务,序号:" + index);
});
}
}
}
ThreadPoolExecutor
1、线程池状态
注意: ThreadPoolExecutor 使用 int 类型的高 3 位表示线程池状态,低 29 位表示线程数量。目的是仅使用一次 CAS 原子操作进行赋值
2、构造方法
- public ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)
- corePoolSize:核心线程数(最多保留的线程数)
- maximumPoolSize:最大线程数,救援线程数 = 最大线程数 - 核心线程数
- keepAliveTime:生存时间(针对救急线程)
- unit:时间单位(针对救急线程)
- workQueue:阻塞队列
- threadFactory:线程工厂(为线程创建时起名称)
- handler:拒绝策略
- AbortPolicy:让调用者抛出 RejectedExecutionException 异常,这是默认策略
- CallerRunsPolicy:让调用者运行任务
- DiscardPolicy:放弃本次任务
- DiscardOldestPolicy:放弃队列中早的任务,本任务取而代之
3、成员方法
描述 | 方法 |
---|---|
执行任务 | void execute(runnable) |
执行任务,并可获取返回值 | Future submit(callback) |
执行集合中所有任务 | List invokeAll(callbacks) |
执行集合中任一任务 | T invokeAny(callbacks) |
线程是否关闭 | boolean isShutdown() |
线程是否终结 | boolean isTerminated() |
指定时间内终结 | boolean awaitTermination(timeout, unit) |
4、案例
public class ThreadPoolExecutorDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 使用构造函数创建线程池
final ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 1000,
TimeUnit.MILLISECONDS, new SynchronousQueue<>());
// 执行任务
pool.execute(() -> System.out.println("执行任务1"));
// 执行任务有返回值
final Future<Integer> future = pool.submit(() -> {
System.out.println("执行任务2");
return 100;
});
System.out.println("执行结果:" + future.get());
// 执行集合内所有任务
List<Callable<Boolean>> tasks = new ArrayList<>();
tasks.add(() -> {
System.out.println("执行任务3");
return true;
});
List<Future<Boolean>> results1 = pool.invokeAll(tasks);
System.out.println("执行结果:" + results1);
// 执行集合中任一任务
Boolean result = pool.invokeAny(tasks);
System.out.println("执行结果:" + result);
// 其他方法
System.out.println(pool.isShutdown());
System.out.println(pool.isTerminated());
pool.awaitTermination(1, TimeUnit.SECONDS);
// 关闭线程池
pool.shutdown();
}
}
Executors 工具类
1、常用方法
- Executors.newFixedThreadPool(coreSize):创建固定大小的线程池
- Executors.newSingleThreadExecutor():创建单个线程的线程池
- Executors.newCachedThreadPool():创建具有缓存功能的线程池
- Executors.newScheduledThreadPool(coreSize):创建具有调度功能的线程池
2、案例
public class ExecutorsDemo {
public static void main(String[] args) {
// 创建固定大小的线程池
final ExecutorService pool2 = Executors.newFixedThreadPool(2);
// 创建单个线程的线程池
final ExecutorService pool3 = Executors.newSingleThreadExecutor();
// 创建具有缓存功能的线程池
final ExecutorService pool4 = Executors.newCachedThreadPool();
// 创建具有调度功能的线程池
final ScheduledExecutorService pool5 = Executors.newScheduledThreadPool(2);
// 定时任务
pool5.scheduleAtFixedRate(() -> System.out.println("执行定时任务"), 1, 2, TimeUnit.SECONDS);
// 延时任务
pool5.schedule(() -> System.out.println("执行延时任务"), 3, TimeUnit.SECONDS);
}
}
Fork & Join
1、概述
- :将一个大任务拆分为算法上相同的小任务,直至不能拆分可以直接求解。它体现的是一种分治思想,适用于能够进行任务拆分的 cpu 密集型运算
2、案例
/**
* 使用Fork & Join方式 拆分1-5之间的和
*/
public class ForkDemo {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
final Integer value = pool.invoke(new Task(1, 5));
System.out.println("结果:" + value);
}
}
class Task extends RecursiveTask<Integer> {
private final int begin;
private final int end;
public Task(int left, int right) {
this.begin = left;
this.end = right;
}
@Override
protected Integer compute() {
if (begin == end) {
return begin;
}
if (end - begin == 1) {
return begin + end;
}
int mid = (end + begin) / 2;
Task task1 = new Task(begin, mid);
task1.fork();
Task task2 = new Task(mid + 1, end);
task2.fork();
return task1.join() + task2.join();
}
}