add AsyncRetryExecutor

This commit is contained in:
Timi
2025-11-07 11:08:19 +08:00
parent 687e5c3bf1
commit d706574879
2 changed files with 183 additions and 0 deletions

View File

@ -0,0 +1,148 @@
package com.imyeyu.java.thread;
import com.imyeyu.java.bean.Callback;
import com.imyeyu.java.bean.CallbackArg;
import java.util.concurrent.TimeUnit;
/**
*
* @author 夜雨
* @since 2025-11-06 17:45
*/
public class AsyncRetryExecutor {
/** 默认重试次数,-1 表无限 */
private static final int DEFAULT_MAX_RETRY = -1;
/** 默认重试间隔(毫秒) */
private static final long DEFAULT_RETRY_INTERVAL = -1;
/** 线程名前缀 */
private static final String DEFAULT_THREAD_NAME_PREFIX = "AsyncRetryThread-";
/** true 为守护进程 */
private static final boolean DEFAULT_DAEMON = false;
private final Thread thread;
private volatile boolean isSuccess = false;
private volatile boolean isRunning = false;
private AsyncRetryExecutor(Builder builder) {
thread = new Thread(() -> {
int retryCount = 0;
while (isRunning && !isSuccess && (builder.maxRetry < 0 || retryCount <= builder.maxRetry)) {
try {
builder.callback.handler();
isSuccess = true;
} catch (Exception e) {
retryCount++;
if (!isRunning) {
// 中断
break;
}
if (0 < builder.maxRetry && builder.maxRetry < retryCount) {
// 超过重试次数
builder.onRetryExhausted.handler(e);
break;
}
// 重试
try {
TimeUnit.MILLISECONDS.sleep(builder.retryInterval);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
break;
}
}
}
}, builder.threadNamePrefix + Thread.currentThread().threadId());
thread.setDaemon(builder.daemon);
}
/**
* 构造器
*
* @author 夜雨
* @since 2025-11-06 23:37
*/
public static class Builder {
// 必需参数
private final Callback callback;
// 可选参数(带默认值)
private int maxRetry = DEFAULT_MAX_RETRY;
private long retryInterval = DEFAULT_RETRY_INTERVAL;
private String threadNamePrefix = DEFAULT_THREAD_NAME_PREFIX;
private boolean daemon = DEFAULT_DAEMON;
private CallbackArg<Exception> onRetryExhausted;
public Builder(Callback callback) {
this.callback = callback;
}
public Builder maxRetry(int maxRetry) {
this.maxRetry = maxRetry;
return this;
}
public Builder retryInterval(long retryInterval) {
this.retryInterval = retryInterval;
return this;
}
public Builder threadNamePrefix(String prefix) {
this.threadNamePrefix = prefix;
return this;
}
public Builder daemon(boolean daemon) {
this.daemon = daemon;
return this;
}
public Builder onRetryExhausted(CallbackArg<Exception> onRetryExhausted) {
this.onRetryExhausted = onRetryExhausted;
return this;
}
public AsyncRetryExecutor build() {
return new AsyncRetryExecutor(this);
}
}
public static AsyncRetryExecutor create(Callback callback) {
return new Builder(callback).build();
}
public static AsyncRetryExecutor create(Callback callback, int maxRetry) {
return new Builder(callback).maxRetry(maxRetry).build();
}
public static AsyncRetryExecutor create(Callback callback, long interval) {
return new Builder(callback).retryInterval(interval).build();
}
public void start() {
if (!isRunning) {
isRunning = true;
isSuccess = false;
thread.start();
}
}
public void interrupt() {
isRunning = false;
if (thread.isAlive() && !thread.isInterrupted()) {
thread.interrupt();
}
}
public boolean isSuccess() {
return isSuccess;
}
public boolean isRunning() {
return isRunning;
}
}

View File

@ -0,0 +1,35 @@
package test;
import com.imyeyu.java.thread.AsyncRetryExecutor;
import org.junit.jupiter.api.Test;
/**
* @author 夜雨
* @since 2025-11-06 23:59
*/
public class TestThread {
@Test
public void testAsyncRetryExecutor() throws InterruptedException {
Object lock = new Object();
AsyncRetryExecutor executor = AsyncRetryExecutor.create(() -> {
double value = Math.random();
if (.9 < value) {
System.out.println("success " + value);
synchronized (lock) {
lock.notifyAll();
}
} else {
System.out.println("fail " + value);
throw new RuntimeException("fail");
}
});
executor.start();
synchronized (lock) {
lock.wait();
}
assert executor.isSuccess();
}
}