cn.hutool.core.thread.ThreadUtil Maven / Gradle / Ivy
package cn.hutool.core.thread;
import cn.hutool.core.util.RuntimeUtil;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* 线程池工具
*
* @author luxiaolei
*/
public class ThreadUtil {
/**
* 新建一个线程池,默认的策略如下:
*
* 1. 初始线程数为corePoolSize指定的大小
* 2. 没有最大线程数限制
* 3. 默认使用LinkedBlockingQueue,默认队列大小为1024
*
*
* @param corePoolSize 同时执行的线程数大小
* @return ExecutorService
*/
public static ExecutorService newExecutor(int corePoolSize) {
ExecutorBuilder builder = ExecutorBuilder.create();
if (corePoolSize > 0) {
builder.setCorePoolSize(corePoolSize);
}
return builder.build();
}
/**
* 获得一个新的线程池,默认的策略如下:
*
* 1. 初始线程数为 0
* 2. 最大线程数为Integer.MAX_VALUE
* 3. 使用SynchronousQueue
* 4. 任务直接提交给线程而不保持它们
*
*
* @return ExecutorService
*/
public static ExecutorService newExecutor() {
return ExecutorBuilder.create().useSynchronousQueue().build();
}
/**
* 获得一个新的线程池,只有单个线程,策略如下:
*
* 1. 初始线程数为 1
* 2. 最大线程数为 1
* 3. 默认使用LinkedBlockingQueue,默认队列大小为1024
* 4. 同时只允许一个线程工作,剩余放入队列等待,等待数超过1024报错
*
*
* @return ExecutorService
*/
public static ExecutorService newSingleExecutor() {
return ExecutorBuilder.create()//
.setCorePoolSize(1)//
.setMaxPoolSize(1)//
.setKeepAliveTime(0)//
.buildFinalizable();
}
/**
* 获得一个新的线程池
* 如果maximumPoolSize >= corePoolSize,在没有新任务加入的情况下,多出的线程将最多保留60s
*
* @param corePoolSize 初始线程池大小
* @param maximumPoolSize 最大线程池大小
* @return {@link ThreadPoolExecutor}
*/
public static ThreadPoolExecutor newExecutor(int corePoolSize, int maximumPoolSize) {
return ExecutorBuilder.create()
.setCorePoolSize(corePoolSize)
.setMaxPoolSize(maximumPoolSize)
.build();
}
/**
* 获得一个新的线程池,并指定最大任务队列大小
* 如果maximumPoolSize >= corePoolSize,在没有新任务加入的情况下,多出的线程将最多保留60s
*
* @param corePoolSize 初始线程池大小
* @param maximumPoolSize 最大线程池大小
* @param maximumQueueSize 最大任务队列大小
* @return {@link ThreadPoolExecutor}
* @since 5.4.1
*/
public static ExecutorService newExecutor(int corePoolSize, int maximumPoolSize, int maximumQueueSize) {
return ExecutorBuilder.create()
.setCorePoolSize(corePoolSize)
.setMaxPoolSize(maximumPoolSize)
.setWorkQueue(new LinkedBlockingQueue<>(maximumQueueSize))
.build();
}
/**
* 获得一个新的线程池
* 传入阻塞系数,线程池的大小计算公式为:CPU可用核心数 / (1 - 阻塞因子)
* Blocking Coefficient(阻塞系数) = 阻塞时间/(阻塞时间+使用CPU的时间)
* 计算密集型任务的阻塞系数为0,而IO密集型任务的阻塞系数则接近于1。
*
* see: http://blog.csdn.net/partner4java/article/details/9417663
*
* @param blockingCoefficient 阻塞系数,阻塞因子介于0~1之间的数,阻塞因子越大,线程池中的线程数越多。
* @return {@link ThreadPoolExecutor}
* @since 3.0.6
*/
public static ThreadPoolExecutor newExecutorByBlockingCoefficient(float blockingCoefficient) {
if (blockingCoefficient >= 1 || blockingCoefficient < 0) {
throw new IllegalArgumentException("[blockingCoefficient] must between 0 and 1, or equals 0.");
}
// 最佳的线程数 = CPU可用核心数 / (1 - 阻塞系数)
int poolSize = (int) (RuntimeUtil.getProcessorCount() / (1 - blockingCoefficient));
return ExecutorBuilder.create().setCorePoolSize(poolSize).setMaxPoolSize(poolSize).setKeepAliveTime(0L).build();
}
/**
* 获取一个新的线程池,默认的策略如下
*
* 1. 核心线程数与最大线程数为nThreads指定的大小
* 2. 默认使用LinkedBlockingQueue,默认队列大小为1024
* 3. 如果isBlocked为{code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
*
*
* @param nThreads 线程池大小
* @param threadNamePrefix 线程名称前缀
* @param isBlocked 是否使用{@link BlockPolicy}策略
* @return ExecutorService
* @author luozongle
* @since 5.8.0
*/
public static ExecutorService newFixedExecutor(int nThreads, String threadNamePrefix, boolean isBlocked) {
return newFixedExecutor(nThreads, 1024, threadNamePrefix, isBlocked);
}
/**
* 获取一个新的线程池,默认的策略如下
*
* 1. 核心线程数与最大线程数为nThreads指定的大小
* 2. 默认使用LinkedBlockingQueue
* 3. 如果isBlocked为{code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
*
*
* @param nThreads 线程池大小
* @param maximumQueueSize 队列大小
* @param threadNamePrefix 线程名称前缀
* @param isBlocked 是否使用{@link BlockPolicy}策略
* @return ExecutorService
* @author luozongle
* @since 5.8.0
*/
public static ExecutorService newFixedExecutor(int nThreads, int maximumQueueSize, String threadNamePrefix, boolean isBlocked) {
return newFixedExecutor(nThreads, maximumQueueSize, threadNamePrefix,
(isBlocked ? RejectPolicy.BLOCK : RejectPolicy.ABORT).getValue());
}
/**
* 获得一个新的线程池,默认策略如下
*
* 1. 核心线程数与最大线程数为nThreads指定的大小
* 2. 默认使用LinkedBlockingQueue
*
*
* @param nThreads 线程池大小
* @param maximumQueueSize 队列大小
* @param threadNamePrefix 线程名称前缀
* @param handler 拒绝策略
* @return ExecutorService
* @author luozongle
* @since 5.8.0
*/
public static ExecutorService newFixedExecutor(int nThreads,
int maximumQueueSize,
String threadNamePrefix,
RejectedExecutionHandler handler) {
return ExecutorBuilder.create()
.setCorePoolSize(nThreads).setMaxPoolSize(nThreads)
.setWorkQueue(new LinkedBlockingQueue<>(maximumQueueSize))
.setThreadFactory(createThreadFactory(threadNamePrefix))
.setHandler(handler)
.build();
}
/**
* 直接在公共线程池中执行线程
*
* @param runnable 可运行对象
*/
public static void execute(Runnable runnable) {
GlobalThreadPool.execute(runnable);
}
/**
* 执行异步方法
*
* @param runnable 需要执行的方法体
* @param isDaemon 是否守护线程。守护线程会在主线程结束后自动结束
* @return 执行的方法体
*/
public static Runnable execAsync(Runnable runnable, boolean isDaemon) {
Thread thread = new Thread(runnable);
thread.setDaemon(isDaemon);
thread.start();
return runnable;
}
/**
* 执行有返回值的异步方法
* Future代表一个异步执行的操作,通过get()方法可以获得操作的结果,如果异步操作还没有完成,则,get()会使当前线程阻塞
*
* @param 回调对象类型
* @param task {@link Callable}
* @return Future
*/
public static Future execAsync(Callable task) {
return GlobalThreadPool.submit(task);
}
/**
* 执行有返回值的异步方法
* Future代表一个异步执行的操作,通过get()方法可以获得操作的结果,如果异步操作还没有完成,则,get()会使当前线程阻塞
*
* @param runnable 可运行对象
* @return {@link Future}
* @since 3.0.5
*/
public static Future> execAsync(Runnable runnable) {
return GlobalThreadPool.submit(runnable);
}
/**
* 新建一个CompletionService,调用其submit方法可以异步执行多个任务,最后调用take方法按照完成的顺序获得其结果。
* 若未完成,则会阻塞
*
* @param 回调对象类型
* @return CompletionService
*/
public static CompletionService newCompletionService() {
return new ExecutorCompletionService<>(GlobalThreadPool.getExecutor());
}
/**
* 新建一个CompletionService,调用其submit方法可以异步执行多个任务,最后调用take方法按照完成的顺序获得其结果。
* 若未完成,则会阻塞
*
* @param 回调对象类型
* @param executor 执行器 {@link ExecutorService}
* @return CompletionService
*/
public static CompletionService newCompletionService(ExecutorService executor) {
return new ExecutorCompletionService<>(executor);
}
/**
* 新建一个CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
*
* @param threadCount 线程数量
* @return CountDownLatch
*/
public static CountDownLatch newCountDownLatch(int threadCount) {
return new CountDownLatch(threadCount);
}
/**
* 创建新线程,非守护线程,正常优先级,线程组与当前线程的线程组一致
*
* @param runnable {@link Runnable}
* @param name 线程名
* @return {@link Thread}
* @since 3.1.2
*/
public static Thread newThread(Runnable runnable, String name) {
final Thread t = newThread(runnable, name, false);
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
/**
* 创建新线程
*
* @param runnable {@link Runnable}
* @param name 线程名
* @param isDaemon 是否守护线程
* @return {@link Thread}
* @since 4.1.2
*/
public static Thread newThread(Runnable runnable, String name, boolean isDaemon) {
final Thread t = new Thread(null, runnable, name);
t.setDaemon(isDaemon);
return t;
}
/**
* 挂起当前线程
*
* @param timeout 挂起的时长
* @param timeUnit 时长单位
* @return 被中断返回false,否则true
*/
public static boolean sleep(Number timeout, TimeUnit timeUnit) {
try {
timeUnit.sleep(timeout.longValue());
} catch (InterruptedException e) {
return false;
}
return true;
}
/**
* 挂起当前线程
*
* @param millis 挂起的毫秒数
* @return 被中断返回false,否则true
*/
public static boolean sleep(Number millis) {
if (millis == null) {
return true;
}
return sleep(millis.longValue());
}
/**
* 挂起当前线程
*
* @param millis 挂起的毫秒数
* @return 被中断返回false,否则true
* @since 5.3.2
*/
public static boolean sleep(long millis) {
if (millis > 0) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
return false;
}
}
return true;
}
/**
* 考虑{@link Thread#sleep(long)}方法有可能时间不足给定毫秒数,此方法保证sleep时间不小于给定的毫秒数
*
* @param millis 给定的sleep时间
* @return 被中断返回false,否则true
* @see ThreadUtil#sleep(Number)
*/
public static boolean safeSleep(Number millis) {
if (millis == null) {
return true;
}
return safeSleep(millis.longValue());
}
/**
* 考虑{@link Thread#sleep(long)}方法有可能时间不足给定毫秒数,此方法保证sleep时间不小于给定的毫秒数
*
* @param millis 给定的sleep时间
* @return 被中断返回false,否则true
* @see ThreadUtil#sleep(Number)
* @since 5.3.2
*/
public static boolean safeSleep(long millis) {
long done = 0;
long before;
long spendTime;
while (done >= 0 && done < millis) {
before = System.currentTimeMillis();
if (false == sleep(millis - done)) {
return false;
}
spendTime = System.currentTimeMillis() - before;
if (spendTime <= 0) {
// Sleep花费时间为0或者负数,说明系统时间被拨动
break;
}
done += spendTime;
}
return true;
}
/**
* @return 获得堆栈列表
*/
public static StackTraceElement[] getStackTrace() {
return Thread.currentThread().getStackTrace();
}
/**
* 获得堆栈项
*
* @param i 第几个堆栈项
* @return 堆栈项
*/
public static StackTraceElement getStackTraceElement(int i) {
StackTraceElement[] stackTrace = getStackTrace();
if (i < 0) {
i += stackTrace.length;
}
return stackTrace[i];
}
/**
* 创建本地线程对象
*
* @param 持有对象类型
* @param isInheritable 是否为子线程提供从父线程那里继承的值
* @return 本地线程
*/
public static ThreadLocal createThreadLocal(boolean isInheritable) {
if (isInheritable) {
return new InheritableThreadLocal<>();
} else {
return new ThreadLocal<>();
}
}
/**
* 创建本地线程对象
*
* @param 持有对象类型
* @param supplier 初始化线程对象函数
* @return 本地线程
* @see ThreadLocal#withInitial(Supplier)
* @since 5.6.7
*/
public static ThreadLocal createThreadLocal(Supplier extends T> supplier) {
return ThreadLocal.withInitial(supplier);
}
/**
* 创建ThreadFactoryBuilder
*
* @return ThreadFactoryBuilder
* @see ThreadFactoryBuilder#build()
* @since 4.1.13
*/
public static ThreadFactoryBuilder createThreadFactoryBuilder() {
return ThreadFactoryBuilder.create();
}
/**
* 创建自定义线程名称前缀的{@link ThreadFactory}
*
* @param threadNamePrefix 线程名称前缀
* @return {@link ThreadFactory}
* @see ThreadFactoryBuilder#build()
* @since 5.8.0
*/
public static ThreadFactory createThreadFactory(String threadNamePrefix) {
return ThreadFactoryBuilder.create().setNamePrefix(threadNamePrefix).build();
}
/**
* 结束线程,调用此方法后,线程将抛出 {@link InterruptedException}异常
*
* @param thread 线程
* @param isJoin 是否等待结束
*/
public static void interrupt(Thread thread, boolean isJoin) {
if (null != thread && false == thread.isInterrupted()) {
thread.interrupt();
if (isJoin) {
waitForDie(thread);
}
}
}
/**
* 等待当前线程结束. 调用 {@link Thread#join()} 并忽略 {@link InterruptedException}
*/
public static void waitForDie() {
waitForDie(Thread.currentThread());
}
/**
* 等待线程结束. 调用 {@link Thread#join()} 并忽略 {@link InterruptedException}
*
* @param thread 线程
*/
public static void waitForDie(Thread thread) {
if (null == thread) {
return;
}
boolean dead = false;
do {
try {
thread.join();
dead = true;
} catch (InterruptedException e) {
// ignore
}
} while (false == dead);
}
/**
* 获取JVM中与当前线程同组的所有线程
*
* @return 线程对象数组
*/
public static Thread[] getThreads() {
return getThreads(Thread.currentThread().getThreadGroup().getParent());
}
/**
* 获取JVM中与当前线程同组的所有线程
* 使用数组二次拷贝方式,防止在线程列表获取过程中线程终止
* from Voovan
*
* @param group 线程组
* @return 线程对象数组
*/
public static Thread[] getThreads(ThreadGroup group) {
final Thread[] slackList = new Thread[group.activeCount() * 2];
final int actualSize = group.enumerate(slackList);
final Thread[] result = new Thread[actualSize];
System.arraycopy(slackList, 0, result, 0, actualSize);
return result;
}
/**
* 获取进程的主线程
* from Voovan
*
* @return 进程的主线程
*/
public static Thread getMainThread() {
for (Thread thread : getThreads()) {
if (thread.getId() == 1) {
return thread;
}
}
return null;
}
/**
* 获取当前线程的线程组
*
* @return 线程组
* @since 3.1.2
*/
public static ThreadGroup currentThreadGroup() {
final SecurityManager s = System.getSecurityManager();
return (null != s) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
}
/**
* 创建线程工厂
*
* @param prefix 线程名前缀
* @param isDaemon 是否守护线程
* @return {@link ThreadFactory}
* @since 4.0.0
*/
public static ThreadFactory newNamedThreadFactory(String prefix, boolean isDaemon) {
return new NamedThreadFactory(prefix, isDaemon);
}
/**
* 创建线程工厂
*
* @param prefix 线程名前缀
* @param threadGroup 线程组,可以为null
* @param isDaemon 是否守护线程
* @return {@link ThreadFactory}
* @since 4.0.0
*/
public static ThreadFactory newNamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDaemon) {
return new NamedThreadFactory(prefix, threadGroup, isDaemon);
}
/**
* 创建线程工厂
*
* @param prefix 线程名前缀
* @param threadGroup 线程组,可以为null
* @param isDaemon 是否守护线程
* @param handler 未捕获异常处理
* @return {@link ThreadFactory}
* @since 4.0.0
*/
public static ThreadFactory newNamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDaemon, UncaughtExceptionHandler handler) {
return new NamedThreadFactory(prefix, threadGroup, isDaemon, handler);
}
/**
* 阻塞当前线程,保证在main方法中执行不被退出
*
* @param obj 对象所在线程
* @since 4.5.6
*/
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
public static void sync(Object obj) {
synchronized (obj) {
try {
obj.wait();
} catch (InterruptedException e) {
// ignore
}
}
}
/**
* 并发测试
* 此方法用于测试多线程下执行某些逻辑的并发性能
* 调用此方法会导致当前线程阻塞。
* 结束后可调用{@link ConcurrencyTester#getInterval()} 方法获取执行时间
*
* @param threadSize 并发线程数
* @param runnable 执行的逻辑实现
* @return {@link ConcurrencyTester}
* @since 4.5.8
*/
@SuppressWarnings("resource")
public static ConcurrencyTester concurrencyTest(int threadSize, Runnable runnable) {
return (new ConcurrencyTester(threadSize)).test(runnable);
}
/**
* 创建{@link ScheduledThreadPoolExecutor}
*
* @param corePoolSize 初始线程池大小
* @return {@link ScheduledThreadPoolExecutor}
* @since 5.5.8
*/
public static ScheduledThreadPoolExecutor createScheduledExecutor(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
/**
* 开始执行一个定时任务,执行方式分fixedRate模式和fixedDelay模式。
* 注意:此方法的延迟和周期的单位均为毫秒。
*
*
* - fixedRate 模式:以固定的频率执行。每period的时刻检查,如果上个任务完成,启动下个任务,否则等待上个任务结束后立即启动。
* - fixedDelay模式:以固定的延时执行。上次任务结束后等待period再执行下个任务。
*
*
* @param executor 定时任务线程池,{@code null}新建一个默认线程池
* @param command 需要定时执行的逻辑
* @param initialDelay 初始延迟,单位毫秒
* @param period 执行周期,单位毫秒
* @param fixedRateOrFixedDelay {@code true}表示fixedRate模式,{@code false}表示fixedDelay模式
* @return {@link ScheduledThreadPoolExecutor}
* @since 5.5.8
*/
public static ScheduledThreadPoolExecutor schedule(ScheduledThreadPoolExecutor executor,
Runnable command,
long initialDelay,
long period,
boolean fixedRateOrFixedDelay) {
return schedule(executor, command, initialDelay, period, TimeUnit.MILLISECONDS, fixedRateOrFixedDelay);
}
/**
* 开始执行一个定时任务,执行方式分fixedRate模式和fixedDelay模式。
*
*
* - fixedRate 模式:以固定的频率执行。每period的时刻检查,如果上个任务完成,启动下个任务,否则等待上个任务结束后立即启动。
* - fixedDelay模式:以固定的延时执行。上次任务结束后等待period再执行下个任务。
*
*
* @param executor 定时任务线程池,{@code null}新建一个默认线程池
* @param command 需要定时执行的逻辑
* @param initialDelay 初始延迟
* @param period 执行周期
* @param timeUnit 时间单位
* @param fixedRateOrFixedDelay {@code true}表示fixedRate模式,{@code false}表示fixedDelay模式
* @return {@link ScheduledThreadPoolExecutor}
* @since 5.6.5
*/
public static ScheduledThreadPoolExecutor schedule(ScheduledThreadPoolExecutor executor,
Runnable command,
long initialDelay,
long period,
TimeUnit timeUnit,
boolean fixedRateOrFixedDelay) {
if (null == executor) {
executor = createScheduledExecutor(2);
}
if (fixedRateOrFixedDelay) {
executor.scheduleAtFixedRate(command, initialDelay, period, timeUnit);
} else {
executor.scheduleWithFixedDelay(command, initialDelay, period, timeUnit);
}
return executor;
}
}