mirror of
https://github.com/TonyJiangWJ/Auto.js.git
synced 2026-06-06 21:13:08 +08:00
修复js-support中,并发线程池内存泄露的问题
This commit is contained in:
parent
794855a079
commit
5b151aa3cb
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user