修复js-support中,并发线程池内存泄露的问题

This commit is contained in:
TonyJiangWJ 2025-06-24 22:38:19 +08:00
parent 794855a079
commit 5b151aa3cb
4 changed files with 130 additions and 16 deletions

View File

@ -7,22 +7,38 @@ import com.stardust.automator.search.SearchAlgorithm;
import java.lang.reflect.Field;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class AlgorithmChanger {
protected final static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 4, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()
, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setName("parallel-pre-build-t-" + t.getName());
return t;
protected static volatile AutoCloseThreadPool threadPoolExecutor = null;
protected static AutoCloseThreadPool getExecutor() {
if (threadPoolExecutor == null) {
synchronized (AlgorithmChanger.class) {
if (threadPoolExecutor == null) {
threadPoolExecutor = new AutoCloseThreadPool(
4, 4, 60, TimeUnit.SECONDS,
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setName("parallel-pre-build-t-" + t.getName());
return t;
}
}, new Runnable() {
@Override
public void run() {
Log.d(TAG, "线程池关闭");
threadPoolExecutor = null;
}
});
}
}
}
});
return threadPoolExecutor;
}
private static final String TAG = "AlgorithmChanger";
public static boolean enableLogging = false;

View File

@ -0,0 +1,81 @@
package com.tony.autojs.search;
import java.util.concurrent.*;
/**
* 自动关闭的线程池当无任务提交的idleTimeout(timeUnit)之后检查当前线程池是否空闲如果是则关闭线程池
* @author TonyJiangWJ
* @since 2025/1/24
*/
public class AutoCloseThreadPool {
private final ThreadPoolExecutor threadPool;
private final ScheduledExecutorService monitor;
private final long idleTimeout;
private final TimeUnit timeUnit;
private ScheduledFuture<?> shutdownTask;
private final Runnable callback;
public AutoCloseThreadPool(int corePoolSize, int maxPoolSize, long idleTimeout, TimeUnit timeUnit,
ThreadFactory threadFactory,
Runnable callback) {
this.threadPool = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
60L, TimeUnit.SECONDS, // 非核心线程空闲存活时间
new LinkedBlockingQueue<Runnable>(),
threadFactory
);
this.monitor = Executors.newSingleThreadScheduledExecutor();
this.idleTimeout = idleTimeout;
this.timeUnit = timeUnit;
this.callback = callback;
}
public void execute(Runnable task) {
synchronized (this) {
// 提交任务前检查线程池是否已关闭
if (threadPool.isShutdown()) {
throw new RejectedExecutionException("ThreadPool已关闭无法接受新任务");
}
// 取消之前的关闭任务
if (shutdownTask != null && !shutdownTask.isDone()) {
shutdownTask.cancel(false);
}
// 提交任务
threadPool.execute(task);
// 调度新的关闭检查任务
scheduleShutdownCheck();
}
}
private void scheduleShutdownCheck() {
shutdownTask = monitor.schedule(new Runnable() {
@Override
public void run() {
synchronized (AutoCloseThreadPool.this) {
// 检查活动线程数和任务队列
if (threadPool.getActiveCount() == 0 && threadPool.getQueue().isEmpty()) {
// 关闭线程池和监控
threadPool.shutdown();
monitor.shutdown();
// 执行线程池关闭回调
if (callback != null) {
callback.run();
}
}
}
}
}, idleTimeout, timeUnit);
}
public void shutdownNow() {
synchronized (this) {
threadPool.shutdownNow();
monitor.shutdownNow();
}
}
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return threadPool.awaitTermination(timeout, unit);
}
}

View File

@ -54,7 +54,7 @@ public abstract class ParallelPreBuildTreeSearch implements SearchAlgorithm {
final CountDownLatch countDownLatch = new CountDownLatch(parent.getRoot().childCount());
for (int i = 0; i < parent.getRoot().childCount(); i++) {
final int finalI = i;
AlgorithmChanger.threadPoolExecutor.execute(new Runnable() {
AlgorithmChanger.getExecutor().execute(new Runnable() {
@Override
public void run() {
UiObject child = parent.getRoot().child(finalI);
@ -90,7 +90,7 @@ public abstract class ParallelPreBuildTreeSearch implements SearchAlgorithm {
final CountDownLatch countDownLatch = new CountDownLatch(parent.getRoot().childCount());
for (int i = 0; i < parent.getRoot().childCount(); i++) {
final int finalI = i;
AlgorithmChanger.threadPoolExecutor.execute(new Runnable() {
AlgorithmChanger.getExecutor().execute(new Runnable() {
@Override
public void run() {
UiObject child = parent.getRoot().child(finalI);

View File

@ -8,7 +8,6 @@ import com.stardust.autojs.core.accessibility.AccessibilityBridge;
import com.stardust.automator.UiObject;
import com.stardust.automator.filter.Filter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -20,12 +19,15 @@ public class UiObjectTreeBuilder extends ParallelPreBuildTreeSearch {
private AccessibilityBridge mAccessibilityBridge;
public UiObjectTreeBuilder() {
}
public UiObjectTreeBuilder(AccessibilityBridge accessibilityBridge) {
this.mAccessibilityBridge = accessibilityBridge;
}
public List<TreeNode> buildTreeNode() {
List<AccessibilityNodeInfo> roots = mAccessibilityBridge.windowRoots();
public List<TreeNode> buildTreeNode(List<AccessibilityNodeInfo> roots) {
if (BuildConfig.DEBUG)
Log.d(TAG, "find: roots = " + roots);
if (roots.isEmpty()) {
@ -45,8 +47,15 @@ public class UiObjectTreeBuilder extends ParallelPreBuildTreeSearch {
return result;
}
public List<TreeNode> buildVisibleTreeNode() {
public List<TreeNode> buildTreeNode() {
if (mAccessibilityBridge == null) {
return Collections.emptyList();
}
List<AccessibilityNodeInfo> roots = mAccessibilityBridge.windowRoots();
return buildTreeNode(roots);
}
public List<TreeNode> buildVisibleTreeNode(List<AccessibilityNodeInfo> roots) {
if (BuildConfig.DEBUG)
Log.d(TAG, "find: roots = " + roots);
if (roots.isEmpty()) {
@ -66,6 +75,14 @@ public class UiObjectTreeBuilder extends ParallelPreBuildTreeSearch {
return result;
}
public List<TreeNode> buildVisibleTreeNode() {
if (mAccessibilityBridge == null) {
return Collections.emptyList();
}
List<AccessibilityNodeInfo> roots = mAccessibilityBridge.windowRoots();
return buildVisibleTreeNode(roots);
}
@NonNull
@Override
public ArrayList<UiObject> search(@NonNull UiObject root, @NonNull Filter filter, int limit) {