目录
自定义线程池ThreadPoolExecutor和缓冲队列BlockingQueue
自定义线程池(ThreadPoolExecutor和BlockingQueue连用)
线程池的返回值ExecutorService简介
ExecutorService是Java提供的用于管理线程池的类。该类的两个作用:控制线程数量和重用线程
四种常见的线程池详
Executors.newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
Executors.newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。特点是他的核心线程数和最大线程数是一样的,你可以把它看作成是一个固定线程数的线程池,因为他不会去将超出线程数的线程缓存到队列中,如果超出线程数了,就会按线程拒绝策略来执行。
Executors.newScheduledThreadPool:创建一个周期性线程池,支持定时及周期性任务执行。
Executors.newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
public class ThreadDemo {
public static void main(String[] args){
//创建一个可重用固定个数的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
//打印正在执行的缓存线程信息
System.out.println(Thread.currentThread().getName()+"正在被执行");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
executor.shutdown();
}
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
Runnable task03 = new Runnable() {
@Override
public void run() {
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
System.out.println(time+":scheduleWithFixedDelay 开始执行");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
System.out.println(time+":scheduleWithFixedDelay 执行完成了");
}
};
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
System.out.println(time+":开始执行");
service.scheduleWithFixedDelay(task03, 10, 10, TimeUnit.SECONDS);
}
}
自定义线程池ThreadPoolExecutor和缓冲队列BlockingQueue
缓冲队列BlockingQueue简介:
BlockingQueue是双缓冲队列。BlockingQueue内部使用两条队列,允许两个线程同时向队列一个存储,一个取出操作。在保证并发安全的同时,提高了队列的存取效率。
常用的几种BlockingQueue:
ArrayBlockingQueue(int i):规定大小的BlockingQueue,其构造必须指定大小。其所含的对象是FIFO顺序排序的。
LinkedBlockingQueue()或者(int i):大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是FIFO顺序排序的。
PriorityBlockingQueue()或者(int i):类似于LinkedBlockingQueue,但是其所含对象的排序不是FIFO,而是依据对象的自然顺序或者构造函数的Comparator决定。
SynchronizedQueue():特殊的BlockingQueue,对其的操作必须是放和取交替完成。
自定义线程池(ThreadPoolExecutor和BlockingQueue连用)
自定义线程池,可以用ThreadPoolExecutor类创建,它有多个构造方法来创建线程池。
常见的构造函数:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
参数介绍:
名称 | 类型 | 含义 |
corePoolSize | int | 核心线程池大小 如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会被超时回收; threadPoolExecutor.allowCoreThreadTimeOut(true); |
maximumPoolSize | int | 最大线程池大小 当活跃线程数达到该数值后,后续的新任务将会阻塞。如果线程数超过了核心线程数,那么会创建新的线程,最多就只能创建这么多; |
keepAliveTime | long | 线程最大空闲时间 如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。 |
unit | TimeUnit | 时间单位 TimeUnit.MILLISECONDS(毫秒)TimeUnit.SECONDS(秒) TimeUnit.MINUTES(分)。 |
workQueue | BlockingQueue<Runnable> | 线程等待队列 通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。 new ArrayBlockingQueue<Runnable>(20); |
threadFactory | ThreadFactory | 线程创建工厂 用于指定为线程池创建新线程的方式 |
handler | RejectedExecutionHandler | 拒绝策略 当达到最大线程数时需要执行的饱和策略。默认丢弃任务并抛出 RejectedExecutionException 异常。 |
模拟线程池应用场景
例1 : 简单使用
public class TempThread implements Runnable{
@Override
public void run() {
// 打印正在执行的缓存线程信息
System.out.println(Thread.currentThread().getName() + "正在被执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TestThreadPoolExecutor {
public static void main(String[] args) {
// 创建数组型缓冲等待队列
BlockingQueue<Runnable> bq = new ArrayBlockingQueue<Runnable>(10);
// ThreadPoolExecutor:创建自定义线程池,池中保存的线程数为2,允许最大的线程数为4
ThreadPoolExecutor te = new ThreadPoolExecutor(2, 4, 50, TimeUnit.MILLISECONDS, bq);
// 模拟创建2个任务
for (int i = 0; i < 2; i++) {
te.execute(new TempThread())
}
// 关闭自定义线程池
te .shutdown();
}
}
例2: 多线程的真实场景应用
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.local_project.dto.ThreadRange;
import com.example.local_project.entity.TMonkeykingDevice;
import com.example.local_project.service.ITMonkeykingAreaService;
import com.example.local_project.service.TMonkeykingDeviceService;
import com.example.local_project.utils.ExecutorServiceUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 〈多线程〉
*
* @author 郭兵
* @create 2022/8/2
*/
@SpringBootTest
@Slf4j
public class ExecutorThread {
/**
* 并行启动N个线程执行
*/
private final int THREAD_NUM = 5;
/**
* 每次处理数据条数
*/
private final int PAGE_SIZE = 50;
/**
* id最大值
*/
private final long LONG_MAX = 9223372036854775807L;
@Autowired
TMonkeykingDeviceService tMonkeykingDeviceService;
@Autowired
ITMonkeykingAreaService monkeykingAreaService;
//推荐
@Test
//方式一 线程池工具类
public void test1(){
ExecutorServiceUtil.setCorePoolSize(THREAD_NUM * 3);
//获取线程的启止区间
ThreadRange[] threadRangeArray = getThreadRange();
for (int i = 0; i < THREAD_NUM; i++) {
final int threadIndex = i;
ExecutorServiceUtil.execute(() ->
dealData(threadRangeArray[threadIndex].getStart(), threadRangeArray[threadIndex].getEnd()));
}
}
// 不推荐使用此方法,容易出现oom异常
@Test
public void test2(){
//创建一个线程
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_NUM * 3);
//获取线程的启止区间
ThreadRange[] threadRangeArray = getThreadRange();
for (int i = 0; i < THREAD_NUM; i++) {
final int threadIndex = i;
//执行线程
executorService.execute(() ->
dealData(threadRangeArray[threadIndex].getStart(), threadRangeArray[threadIndex].getEnd()));
}
}
//通过程序计数器 保证线程全部执行成功
@Test
public void test3() throws InterruptedException {
// ExecutorService executorService = Executors.newFixedThreadPool(THREAD_NUM * 3);
ExecutorService executorService = ExecutorServiceUtil.getThreadPool();
//2个线程
ExecutorServiceUtil.setCorePoolSize(THREAD_NUM * 3);
//获取线程的启止区间
ThreadRange[] threadRangeArray = getThreadRange();
//程序计数器
CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
for (int i = 0; i < THREAD_NUM; i++) {
final int threadIndex = i;
executorService.submit(() -> {
dealData(threadRangeArray[threadIndex].getStart(), threadRangeArray[threadIndex].getEnd());
countDownLatch.countDown();
});
}
//线程池 等待10s
executorService.awaitTermination(10, TimeUnit.SECONDS);
//关闭线程 其实是将线程状态设置为中断标志 必须等待所有线程处理完任务,才能完全关闭
executorService.shutdown();
//必须等待两个线程执行完 会一直等待下去,当然也可以设置指定时间等待超时 await(timeout);
countDownLatch.await();
}
private void dealData(long startId, long endId) {
// 每次查询的起始游标
long id = startId;
System.out.println("ExecutorThread.dealData 开始执行 startId:"+startId+" ,endId{"+endId+"}");
while (true) {
//查询最后一条记录id
Page page=new Page(1,PAGE_SIZE);
Page page1 = tMonkeykingDeviceService.page(page, new LambdaQueryWrapper<TMonkeykingDevice>()
.between(TMonkeykingDevice::getId,id,endId).orderByAsc(TMonkeykingDevice::getId));
List<TMonkeykingDevice> deviceList = page1.getRecords();
if (null != deviceList && deviceList.size() > 0) {
System.out.println("dealData_开始处理");
id=deviceList.get(deviceList.size()-1).getId();
}else{
System.out.println("dealData_处理结束了,开始处理");
break;
}
}
}
private ThreadRange[] getThreadRange() {
//定义一个指定大小的数组
ThreadRange[] threadRangeArray = new ThreadRange[THREAD_NUM];
//查询数据库 获取本次操作的总条数
long count = tMonkeykingDeviceService.count(new LambdaQueryWrapper<TMonkeykingDevice>()
.eq(TMonkeykingDevice::getCustomerId, 813975007076992L));
//每次处理N条数据
long eachThreadTotal = count / THREAD_NUM + 1;
long id = 0;
for (int i = 0; i < THREAD_NUM; i++) {
ThreadRange threadRange = new ThreadRange();
//设置数组处理的最小id
threadRange.setStart(id);
//查询最后一条记录id
Page page=new Page(eachThreadTotal,1);
Page page1 = tMonkeykingDeviceService.page(page, new LambdaQueryWrapper<TMonkeykingDevice>()
.gt(TMonkeykingDevice::getId, id).orderByAsc(TMonkeykingDevice::getId));
List<TMonkeykingDevice> records = page1.getRecords();
//设置数组处理的最小id
if (null != records && records.size() > 0) {
id = records.get(0).getId();
threadRange.setEnd(id);
} else {
threadRange.setEnd(LONG_MAX);
}
threadRangeArray[i]=threadRange;
}
return threadRangeArray;
}
//线程池分批处理
@Test
public void test4() {
long id = 0;
while (true) {
//分页查询出需要处理的数据
Page page1 = tMonkeykingDeviceService.page(page, new LambdaQueryWrapper<TMonkeykingDevice>()
.gt(TMonkeykingDevice::getId, id).orderByAsc(TMonkeykingDevice::getId));
List<TMonkeykingDevice> records = page1.getRecords();
if (CollectionUtils.isEmpty(records)) {
break;
}
id = records.get(0).getId();
//开启线程池处理数据
ThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("thread-CarRangeAddAllJob-%s").build();
ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_NUM , THREAD_NUM , 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(list.size()), threadFactory);
try {
CountDownLatch countDownLatch = new CountDownLatch(records.size());
list.forEach(phoenixCar -> executor.execute(() -> {
try {
//处理逻辑
addCarRegionRelation(records);
} catch (Exception e) {
logger.error("异常", e);
} finally {
countDownLatch.countDown();
}
}));
countDownLatch.await();
} catch (Exception e) {
logger.error("异常 ", e);
} finally {
executor.shutdown();
}
}
}
涉及工具类 : ExecutorServiceUtil
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 〈线程池工具类〉
*
* @author lzj
* @create 2019/8/22
*/
public class ExecutorServiceUtil {
private static Lock LOCK = new ReentrantReadWriteLock().readLock();
private static int corePoolSize = 1;
private static int maximumPoolSize = 32;
private static long keepAliveTime = 0L;
private static BlockingQueue<Runnable> workQueue = null;
private static ExecutorService threadPool;
/**
* 由于整个JVM 使用一个线程池,如果关闭后续都关闭了,因此不关闭;
* 随着整个JVM的生命周期而存在
*/
private static void shutdown() {
if (null != threadPool) {
threadPool.shutdown();
}
}
public static void execute(Runnable runnable) {
getThreadPool().execute(runnable);
}
private static ExecutorService getThreadPool() {
if (null == threadPool) {
try {
LOCK.lock();
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("executor-pool-%d").build();
if (null == workQueue) {
workQueue = new LinkedBlockingQueue<>(64);
}
threadPool = new ThreadPoolExecutor(getCorePoolSize(), getMaximumPoolSize(), getKeepAliveTime(),
TimeUnit.MILLISECONDS, workQueue, namedThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
} catch (Exception e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
}
return threadPool;
}
public static int getCorePoolSize() {
return corePoolSize;
}
public static void setCorePoolSize(int corePoolSize) {
ExecutorServiceUtil.corePoolSize = corePoolSize;
}
public static int getMaximumPoolSize() {
return maximumPoolSize;
}
public static void setMaximumPoolSize(int maximumPoolSize) {
ExecutorServiceUtil.maximumPoolSize = maximumPoolSize;
}
public static long getKeepAliveTime() {
return keepAliveTime;
}
public static void setKeepAliveTime(long keepAliveTime) {
ExecutorServiceUtil.keepAliveTime = keepAliveTime;
}
public static BlockingQueue<Runnable> getWorkQueue() {
return workQueue;
}
public static void setWorkQueue(BlockingQueue<Runnable> workQueue) {
ExecutorServiceUtil.workQueue = workQueue;
}
}
/**
* 线程处理实体类
*
*/
public class ThreadRange {
//处理最小id
private long start;
//处理最大id
private long end;
public ThreadRange() {
}
public ThreadRange(long start, long end) {
this.start = start;
this.end = end;
}
public long getStart() {
return start;
}
public void setStart(long start) {
this.start = start;
}
public long getEnd() {
return end;
}
public void setEnd(long end) {
this.end = end;
}
}