threads 线程与同步
threads 线程与同步
threads 是 ScriptX 里负责“开工作线程、管理线程池、在线程上挂定时器、做同步控制、在线程间传结果”的全局对象。
它和普通网页里的 Promise、setTimeout 不是一个层级的概念。这里的 threads.start(...) 真会拉起一个独立的脚本工作线程,而且那个线程自己还能再挂 setTimeout、setInterval、setImmediate。
这页会把下面这些最容易让人混的地方都讲清楚:
threads.start(...)返回的到底是什么对象。- 全局定时器和线程对象上的定时器分别挂在哪个线程上。
threads.exit()在主线程和工作线程里行为为什么不一样。disposable、atomic、lock、condition各适合解决什么问题。threads.pool(...)、JavaFuture和普通threads.start(...)应该怎么选。Promise.prototype.wait()什么时候能用,什么时候会拒绝阻塞。sync(func, lock?)和 Java 锁对象之间是什么关系。
先记住这 14 条
threads是全局对象,直接写threads.start(...)就能用。threads.start(action)接受 JS 函数或 JavaRunnable,会立即启动一个独立工作线程。- 全局
setTimeout/setInterval/setImmediate会挂到“当前所在的脚本线程”上,不一定总在主线程。 thread.setTimeout(...)这类方法是显式把任务挂到某个线程句柄上。- 定时器回调现在支持额外参数,
setTimeout(fn, 100, a, b)这种写法是有效的。 threads.shutDownAll()只停止threads.start(...)创建的独立工作线程,不会关闭线程池。threads.exit()无论从主线程还是工作线程调用,都会停止整个 threads host、线程池和定时器,并永久拒绝再创建新线程。threads.pool(...)返回 JavaExecutorService兼容线程池,适合一批短任务和受控并行。pool.submit(...)返回 JavaFuture,可以取结果、超时等待或取消任务。threads.disposable()最适合做“线程 A 算完结果,线程 B 等待接收”。threads.atomic()返回 JavaAtomicLong,适合简单计数,不适合存复杂对象。threads.lock()返回 JavaReentrantLock,newCondition()使用 Java 原生同步语义。Promise.prototype.wait()已由 ScriptX 注入,可同步等待 Promise;但主调度器回调里不能等待一个尚未完成的 Promise。sync(func, lock?)是最省事的同步入口,不想手写lock.lock()/unlock()时优先用它。
threads.start(action)
启动一个新的脚本工作线程。
参数
| 参数 | 类型 | 可填值 | 说明 |
|---|---|---|---|
action | function | java.lang.Runnable | JS 函数或 Java Runnable | 线程入口,必须且只能传 1 个参数 |
返回值
thread
这里返回的不是 Java 原生 Thread,而是 ScriptX 暴露出来的线程句柄对象。后面这组方法都挂在它上面:
thread.waitFor()thread.join(...)thread.interrupt()thread.isAlive()thread.isInterrupted()thread.getName()/thread.setName(...)thread.getId()/thread.getState()/thread.getPriority()thread.setTimeout(...)thread.setInterval(...)thread.setImmediate(...)
说明
- 每次调用都会创建一个新的单线程调度器。
- 线程名字形如
Spawn-1、Spawn-2。 - 线程入口函数会立即开始跑,不需要你再手动
start()。 - 线程入口函数结束后,如果它自己挂的异步任务也都执行完了,这个线程才算真正结束。
示例
const worker = threads.start(function () {
log("worker started");
sleep(300);
log("worker finished");
});
worker.join();
log("main done");
threads.shutDownAll()
中断当前还活着的所有独立工作线程。
参数
无。
返回值
undefined
说明
- 只处理通过
threads.start(...)拉起来的工作线程。 - 不会关闭
threads.pool(...)创建的线程池;线程池应调用自己的shutdown()/shutdownNow()。 - 不会把“主脚本线程”一起关掉。
- 每个线程上已经挂的
setTimeout/setInterval/setImmediate也会一起取消。
示例
threads.start(function () {
while (true) {
sleep(1000);
}
});
sleep(200);
threads.shutDownAll();
threads.currentThread()
拿到“当前代码正在运行的脚本线程句柄”。
参数
无。
返回值
通常返回 ScriptX thread 句柄;如果当前代码来自 ScriptX 未登记的外部 Java 线程,则返回原始 java.lang.Thread。
说明
- 如果你现在在主脚本里调用,拿到的是主线程句柄。
- 如果你现在在
threads.start(...)的工作线程里调用,拿到的是那个工作线程句柄。 - 在线程池任务里调用,会拿到当前池工作线程对应的句柄,线程内定时器也会归这个逻辑句柄管理。
- 在未登记的外部 Java 回调线程里调用,拿到原始 Java
Thread;它没有 ScriptX 句柄上的setTimeout(...)等扩展方法。 - 这个方法很适合写“我不关心自己现在在哪个线程里,但我想把后续任务继续挂在当前线程上”。
示例
const self = threads.currentThread();
self.setImmediate(function () {
log("still on current thread");
});
threads.getMainThread()
拿到创建当前 ScriptX 运行时的原始 Java 主线程。
参数
无。
返回值
java.lang.Thread
说明
- 不管现在在哪个工作线程里,返回的都是同一个原始
java.lang.Thread。 - 它支持 Java Thread 的
getName()、getId()、getState()等方法,但不带 ScriptX 句柄专用的setImmediate(...)。 - 如果要从工作线程把回调调度到 ScriptX 主调度器,应在主脚本开始时保存
threads.currentThread()返回的主调度句柄。
示例
const mainDispatcher = threads.currentThread();
const rawMainThread = threads.getMainThread();
log(rawMainThread.getName());
threads.start(function () {
mainDispatcher.setImmediate(function () {
log("back on main thread");
});
});
threads.allThreads()
列出当前登记的主线程和独立工作线程。
参数
无。
返回值
java.util.ArrayList
说明
- 第 0 项固定是
threads.getMainThread()返回的原始 Java 主线程。 - 后面是仍登记在运行时中的
threads.start(...)线程句柄。 - 线程池内部工作线程不会出现在这个列表里。
- 返回的是 Java List 包装,不是原生 JS 数组;使用
size()/get(index),不要依赖.length。
示例
const list = threads.allThreads();
log(`count=${list.size()}`);
log(`main=${list.get(0).getName()}`);
threads.hasRunningThreads()
判断当前是否还有独立工作线程,或任一线程池仍有运行/排队任务。
参数
无。
返回值
boolean
示例
if (threads.hasRunningThreads()) {
log("还有后台线程没跑完");
}
threads.exit()
停止整个 threads host。
参数
无。
返回值
通常不返回有效结果;调用后当前运行时进入退出状态。
说明
- 中断全部
threads.start(...)工作线程。 - 对全部
threads.pool(...)执行强制停止。 - 取消归属这些线程/线程池的定时任务。
- 将 threads host 标记为退出;后续
threads.start(...)/threads.pool(...)会抛script exiting。 - 即使从工作线程里调用,也不是“只退出自己”。只想停一个线程时,对那个句柄调用
interrupt()。
示例
threads.start(function () {
log("before exit");
threads.exit();
});
threads.disposable()
创建一个“可阻塞等待的单值容器”。
参数
无。
返回值
disposable
什么时候适合用
- 工作线程算完结果,主线程想阻塞等结果。
- 某个线程要“等另一个线程发信号”。
- 你只需要传 1 份结果,不想上锁、不想自己写条件变量。
示例
const box = threads.disposable();
threads.start(function () {
sleep(500);
box.setAndNotify({ ok: true, value: 123 });
});
const result = box.blockedGet(1000);
log(JSON.stringify(result));
threads.atomic(initial?)
创建一个原子整数对象。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
initial | number | 任意数字 | 0 | 初始值 |
返回值
atomic
说明
- 当前返回的是 Java
AtomicLong。 - 适合做计数器、累计次数、跨线程自增。
- 如果你传的不是数字,会回退到
0。
示例
const counter = threads.atomic(0);
threads.start(function () {
counter.incrementAndGet();
});
threads.start(function () {
counter.incrementAndGet();
});
threads.lock()
创建一个可重入锁。
参数
无。
返回值
lock
说明
- 当前返回的是 Java
ReentrantLock。 - 如果你需要更细粒度的同步控制,可以配合
newCondition()用。 - 如果你只是想把一个函数变成“同一时刻只允许一个线程进来”,优先考虑
sync(func, lock?),写起来更省事。
示例
const lock = threads.lock();
lock.lock();
try {
log("critical section");
} finally {
lock.unlock();
}
threads.pool(options?)
创建一个可复用的脚本线程池。适合同时处理多份短任务、批量计算、并发 IO,避免为每个任务都单独创建 threads.start(...) 线程。
支持的调用形式
threads.pool();
threads.pool(null);
threads.pool({ corePoolSize: 1, maxPoolSize: 4, keepAliveTime: 60000 });
threads.pool(1, 4, 60000);
| 参数 / 字段 | 类型 | 默认值 | 限制 | 说明 |
|---|---|---|---|---|
corePoolSize | number | 0 | >= 0 | 常驻核心线程数 |
maxPoolSize | number | 0 | >= 0 且不能小于 core | 0 表示对外最大值为 2147483647 |
keepAliveTime | number | 60000 | >= 0 | 非核心空闲线程保留毫秒数 |
默认 maxPoolSize: 0 时,getMaximumPoolSize() 会返回 2147483647;内部自动扩容上限按设备 CPU 数限制在 2 ~ 32,超过并行度的任务进入队列,不会真的无节制创建二十亿个线程。
返回值
返回 ThreadPool 包装对象,同时兼容 Java ExecutorService / ThreadPoolExecutor 的主要方法。脚本停止时,尚未手动关闭的池会被统一 shutdownNow()。
完整例子
const pool = threads.pool({
corePoolSize: 2,
maxPoolSize: 4,
keepAliveTime: 30000
});
const first = pool.submit(function () {
return "A";
});
const second = pool.submit(function () {
return "B";
});
log(first.get() + second.get()); // AB
pool.shutdown();
pool.awaitTermination(3, java.util.concurrent.TimeUnit.SECONDS);
threads._pool(corePoolSize?, maxPoolSize?, keepAliveTime?)
threads.pool(core, max, keepAlive) 的底层兼容入口,参数顺序和单位相同。新脚本优先使用 threads.pool(...);_pool 保留给兼容代码和底层调用。
pool.execute(action)
提交一个不需要返回值的 JS 函数或 Java Runnable。
- 必须且只能传 1 个参数。
- 返回
undefined。 - 任务抛出的异常会进入 ScriptX 线程错误上报;需要在调用方取回异常时改用
submit()+future.get()。
pool.execute(function () {
log("run in pool");
});
pool.submit(action, result?)
提交任务并返回 Java Future。
| 调用 | Future 最终结果 |
|---|---|
submit(jsFunction) | JS 函数的返回值 |
submit(javaCallable) | Callable.call() 的返回值 |
submit(javaRunnable) | null |
submit(jsFunction, suppliedResult) | 忽略函数返回值,Future 返回 suppliedResult |
submit(javaRunnable, suppliedResult) | Future 返回 suppliedResult |
const future = pool.submit(function () {
return 42;
});
log(future.get()); // 42
pool.invokeAll(tasks, timeout?, unit?)
批量执行 Java Callable 集合,返回 java.util.List<Future>。
- 只支持 1 个参数,或完整的 3 个参数;不能只给 timeout。
tasks必须是 JavaCollection<Callable>。- 带超时时使用 Java
TimeUnit,例如TimeUnit.SECONDS,不能传字符串"SECONDS"。
const Callable = java.util.concurrent.Callable;
const tasks = java.util.Arrays.asList(
new Callable({ call: function () { return 1; } }),
new Callable({ call: function () { return 2; } })
);
const futures = pool.invokeAll(tasks);
log(futures.get(0).get() + futures.get(1).get());
pool.invokeAny(tasks, timeout?, unit?)
返回最先成功完成的一个 Callable 结果。参数形状和 invokeAll(...) 相同;所有任务都失败时会按 Java ExecutorService.invokeAny 语义抛异常。
pool.shutdown()
平滑关闭:不再接受新任务,但已经运行和已经排队的任务继续执行。返回 undefined。
pool.shutdownNow()
请求中断正在运行的任务,并返回尚未开始执行的原始 Runnable 列表。JS submit 任务对应的 Future 会按取消/中断路径结算。
pool.isShutdown()
调用过 shutdown() 或 shutdownNow() 后返回 true。这不等于全部任务已经结束。
pool.isTerminated()
关闭请求已经发出,并且所有任务都结束后返回 true。
pool.isTerminating()
已经关闭但还没有完全终止时返回 true。
pool.awaitTermination(timeout, unit)
阻塞等待线程池终止,返回 boolean:超时前已终止为 true,否则为 false。
pool.shutdown();
const done = pool.awaitTermination(2, java.util.concurrent.TimeUnit.SECONDS);
pool.getActiveCount()
返回当前大约有多少个线程正在执行任务。它是监控值,不适合当作严格同步条件。
pool.getPoolSize()
返回池内当前工作线程数,包括空闲线程。
pool.getQueueSize()
返回当前等待队列里的任务数量。
pool.getCorePoolSize()
读取核心线程数。
pool.setCorePoolSize(size)
动态修改核心线程数。size 必须是非负数字,并且不能超过当前最大线程数。
pool.getMaximumPoolSize()
读取最大线程数。默认池会返回 2147483647,但默认自动扩容仍受设备并行度保护。
pool.setMaximumPoolSize(size)
动态修改最大线程数。值必须大于 0 且不能小于当前核心线程数。
pool.getKeepAliveTime(unit)
按指定 Java TimeUnit 读取空闲线程保活时间。
const ms = pool.getKeepAliveTime(java.util.concurrent.TimeUnit.MILLISECONDS);
pool.setKeepAliveTime(time, unit)
修改保活时间;time 不能为负数。若已允许核心线程超时,保活时间不能设为 0。
pool.getQueue()
返回线程池真实的 Java BlockingQueue<Runnable>。可以用 size() 查看数量,或用 remove(task) / clear() 操作未执行任务;外部移除队列任务时,ScriptX 会同步释放对应运行时占用。
pool.remove(task)
从等待队列移除一个尚未开始的 Java Runnable,成功返回 true。已经被工作线程领取的任务不能再移除。
pool.getThreadFactory()
返回当前 Java ThreadFactory。
pool.setThreadFactory(factory)
设置 Java ThreadFactory。参数必须真的是 java.util.concurrent.ThreadFactory;可以用 createProxy / JavaAdapter 实现接口。
pool.getRejectedExecutionHandler()
返回当前 Java RejectedExecutionHandler。
pool.setRejectedExecutionHandler(handler)
设置拒绝策略。参数必须实现 java.util.concurrent.RejectedExecutionHandler。线程池关闭后再提交任务仍会进入拒绝路径。
pool.getTaskCount()
返回计划执行过的任务总数估计值。
pool.getCompletedTaskCount()
返回已经完成的任务总数估计值。
pool.getLargestPoolSize()
返回线程池生命周期内同时存在过的最大工作线程数。
pool.prestartCoreThread()
提前启动 1 个核心线程;确实启动时返回 true,没有可启动核心线程时返回 false。
pool.prestartAllCoreThreads()
提前启动全部尚未启动的核心线程,返回本次启动数量。
pool.allowsCoreThreadTimeOut()
查询核心线程是否允许在空闲后退出。
pool.allowCoreThreadTimeOut(value)
设置核心线程是否允许超时。开启时 keepAliveTime 必须大于 0。
pool.purge()
从队列清理已经取消的 Future 任务,返回 undefined。当前实现也会在排队 Future 被取消时主动移除,多数场景不需要频繁手动调用。
pool.toString()
返回线程池当前状态字符串,适合临时日志,不要解析它作为稳定数据协议。
pool.corePoolSize
getCorePoolSize() / setCorePoolSize(...) 的可读写属性别名。
pool.maximumPoolSize
getMaximumPoolSize() / setMaximumPoolSize(...) 的可读写属性别名。
pool.activeCount
getActiveCount() 的只读属性别名。
pool.queue
getQueue() 的只读属性别名。
pool.threadFactory
getThreadFactory() / setThreadFactory(...) 的可读写属性别名。
pool.rejectedExecutionHandler
getRejectedExecutionHandler() / setRejectedExecutionHandler(...) 的可读写属性别名。
pool.poolSize
getPoolSize() 的只读属性别名。
pool.largestPoolSize
getLargestPoolSize() 的只读属性别名。
pool.taskCount
getTaskCount() 的只读属性别名。
pool.completedTaskCount
getCompletedTaskCount() 的只读属性别名。
pool.terminating
isTerminating() 的只读属性别名。
pool.terminated
isTerminated() 的只读属性别名。
future.get(timeout?, unit?)
读取 pool.submit(...) 返回的结果。
get():一直等到完成。get(timeout, unit):超时抛 JavaTimeoutException。- 任务抛错时,错误保留在
ExecutionException的 cause 链中。 - 任务取消后调用会抛
CancellationException。
future.cancel(mayInterruptIfRunning)
取消任务并返回是否成功。false 只取消尚未开始的任务;true 还会请求中断正在运行的 ScriptX 回调。
future.isCancelled()
任务已经成功取消时返回 true。
future.isDone()
正常完成、异常完成或取消后都会返回 true。
thread.waitFor()
等待线程真正启动完成。
参数
无。
返回值
undefined
说明
- 这个方法主要防止你在线程还没进入可调度状态时,就急着往上挂任务。
- 只有独立工作线程和线程池任务句柄带这个方法;主调度句柄没有
waitFor()。
示例
const worker = threads.start(function () {
log("ready");
});
worker.waitFor();
worker.setImmediate(function () {
log("after started");
});
thread.join(millis?, nanos?)
等待线程结束。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
millis | number | >= 0 | 0 | 等待毫秒数,0 且未传 nanos 表示一直等 |
nanos | number | 0 ~ 999999 | 0 | 附加纳秒部分,规则与 Java Thread.join 一致 |
返回值
undefined
说明
- 不传参数或
join(0)会一直等到线程结束。 - 负数毫秒、超出
0 ~ 999999的 nanos 会直接抛错。 - 线程不能
join自己,否则会抛cannot join current thread。 - 有 nanos 时会按 Java Thread 规则换算到实际毫秒等待。
示例
const worker = threads.start(function () {
sleep(800);
});
worker.join(300); // 最多等 300ms
log(worker.isAlive());
thread.interrupt()
中断线程,并取消它挂着的定时任务。
参数
无。
返回值
undefined
说明
- 对工作线程:会停止接收新任务、取消已挂定时器、打断线程。
- 对主线程句柄:效果等同于中断整段脚本。
示例
const worker = threads.start(function () {
while (true) {
sleep(1000);
}
});
sleep(300);
worker.interrupt();
thread.isAlive()
判断线程当前是否还活着。
参数
无。
返回值
boolean
说明
- 工作线程:已经启动且还没结束时返回
true。 - 主线程句柄:脚本运行时还活着就返回
true。
示例
const worker = threads.start(function () {
sleep(500);
});
log(worker.isAlive());
worker.join();
log(worker.isAlive());
thread.isInterrupted()
查询该逻辑线程是否收到过中断请求,返回 boolean。也可以读取 thread.interrupted。
thread.getName()
返回当前 Java 工作线程名。独立工作线程默认类似 Spawn-1;线程池线程名包含池编号和 worker 编号。
thread.setName(name)
修改底层 Java 线程名,返回 undefined。name 不能省略。
thread.getId()
返回底层 Java Thread ID。底层线程尚不可用时返回 -1。
thread.getState()
返回 Java Thread.State,常见值包括 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。
thread.getPriority()
返回 Java 线程优先级,通常范围 1 ~ 10,默认 Thread.NORM_PRIORITY 即 5。
thread.setPriority(priority)
修改线程优先级。超出 Java Thread 允许范围时由 Java 层抛 IllegalArgumentException。
thread.isDaemon()
返回底层 Java 线程是否为 daemon 线程。
thread.setDaemon(value)
设置 daemon 标记。threads.start(...) 返回时线程通常已经启动,Java 不允许对已启动线程再改 daemon 状态,因此这类调用可能抛 IllegalThreadStateException;不要把它当普通运行期开关。
thread.getStackTrace()
返回底层 Java 线程的 StackTraceElement[]。适合排查线程卡在哪,但只是一瞬间快照。
thread.getThreadGroup()
返回底层 Java ThreadGroup,不可用时为 null。
thread.getContextClassLoader()
读取线程上下文 ClassLoader。
thread.setContextClassLoader(classLoader)
设置线程上下文类加载器。只接受真实 ClassLoader 或 null,Rhino 包装对象会先解包。
thread.checkAccess()
调用 Java Thread 的兼容访问检查,不接受参数。Android 新版本中它通常没有额外效果,但为了 Java/Auto.js 接口兼容仍然保留。
thread.getUncaughtExceptionHandler()
返回底层 Java Thread.UncaughtExceptionHandler。
thread.setUncaughtExceptionHandler(handler)
设置未捕获异常处理器。参数必须实现 Thread.UncaughtExceptionHandler,也可传 null 清除。
thread.toString()
返回底层 Java Thread 字符串;底层线程暂不可用时返回 Thread[显示名]。
thread.name
getName() / setName(...) 的可读写属性别名。
thread.priority
getPriority() / setPriority(...) 的可读写属性别名。
thread.daemon
isDaemon() / setDaemon(...) 的可读写属性别名,仍受“已启动线程不能修改 daemon”限制。
thread.state
getState() 的只读属性别名。
thread.id
getId() 的只读属性别名。
thread.alive
isAlive() 的只读属性别名。
thread.interrupted
isInterrupted() 的只读属性别名。
thread.stackTrace
getStackTrace() 的只读属性别名。
thread.threadGroup
getThreadGroup() 的只读属性别名。
thread.contextClassLoader
getContextClassLoader() / setContextClassLoader(...) 的可读写属性别名。
thread.uncaughtExceptionHandler
getUncaughtExceptionHandler() / setUncaughtExceptionHandler(...) 的可读写属性别名。
thread.setTimeout(callback, delayMs?, ...args)
在指定线程上挂一个单次定时器。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
callback | function | 回调函数 | 必填 | 不传会抛错 |
delayMs | number | 任意数字 | 0 | 延迟毫秒数 |
...args | any | 任意值 | 无 | 额外传给回调的参数 |
返回值
number
说明
- 回调会在这个
thread对应的线程里执行。 - 如果线程还没真正启动,会抛
thread has not started yet。 - 如果线程已经结束,会抛
thread has already finished。 delayMs负数会按0处理。
示例
const worker = threads.start(function () {});
worker.waitFor();
worker.setTimeout(function (name, count) {
log(name, count);
}, 200, "job", 3);
thread.clearTimeout(id)
取消指定线程上的单次定时器。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
id | number | 任务编号 |
返回值
boolean
示例
const id = threads.currentThread().setTimeout(function () {
log("never");
}, 1000);
threads.currentThread().clearTimeout(id);
thread.setInterval(callback, delayMs?, ...args)
在指定线程上挂一个循环定时器。
参数
和 thread.setTimeout(...) 一样。
返回值
number
说明
- 每次回调执行完,如果线程仍然活着,就会继续调下一轮。
- 如果回调内部抛出异常,当前循环会停止。
示例
const worker = threads.start(function () {});
worker.waitFor();
let n = 0;
const id = worker.setInterval(function () {
n += 1;
log(`tick=${n}`);
}, 300);
thread.clearInterval(id)
取消指定线程上的循环定时器。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
id | number | 任务编号 |
返回值
boolean
示例
const worker = threads.start(function () {});
worker.waitFor();
const id = worker.setInterval(function () {
log("tick");
}, 1000);
worker.clearInterval(id);
thread.setImmediate(callback, ...args)
在指定线程上尽快执行一个回调。
参数
| 参数 | 类型 | 可填值 | 说明 |
|---|---|---|---|
callback | function | 回调函数 | 必填 |
...args | any | 任意值 | 额外传给回调的参数 |
返回值
number
说明
- 当前实现本质上等价于“这个线程上的
setTimeout(callback, 0, ...args)”。 - 它不是微任务,也不是 Promise 队列概念。
- 更适合表达“别阻塞当前这段逻辑,等线程调度一下再做”。
示例
const worker = threads.start(function () {});
worker.waitFor();
worker.setImmediate(function (text) {
log(text);
}, "run soon");
thread.clearImmediate(id)
取消线程上的 setImmediate(...) 任务。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
id | number | 任务编号 |
返回值
boolean
示例
const worker = threads.start(function () {});
worker.waitFor();
const id = worker.setImmediate(function () {
log("never");
});
worker.clearImmediate(id);
setTimeout(callback, delayMs?, ...args)
把单次任务挂到当前逻辑脚本线程,返回数字任务 ID。在线程池回调里调用时,定时器归当前池任务句柄管理;必要时可以迁移到池内其他 worker 执行,但 threads.currentThread() 会反映实际执行线程。
const id = setTimeout(function (name) {
log("hello " + name);
}, 200, "ScriptX");
clearTimeout(id)
按全局任务注册表取消单次任务,返回 boolean。即使调用位置和创建定时器的线程不同,只要 ID 仍有效也可以取消。
setInterval(callback, delayMs?, ...args)
把循环任务挂到当前逻辑脚本线程,返回数字任务 ID。回调抛出未处理异常、所属线程退出或脚本停止时,循环会终止。
clearInterval(id)
取消循环任务并返回 boolean。
Task(callback, delayMs?, ...args)
全局 setTimeout(...) 的兼容别名,参数、额外回调参数和返回任务 ID 完全相同。
setImmediate(callback, ...args)
全局即时调度函数。
参数
和 thread.setImmediate(...) 一样。
返回值
number
说明
- 它会挂到“当前正在执行这段代码的脚本线程”上。
- 如果你当前就在工作线程里,那它不是回主线程,而是仍然挂在当前工作线程上。
- 如果你就是想挂到某个明确线程,优先写
thread.setImmediate(...),更直观。
示例
setImmediate(function (a, b) {
log(a + b);
}, 1, 2);
clearImmediate(id)
取消全局 setImmediate(...) 任务。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
id | number | 任务编号 |
返回值
boolean
示例
const id = setImmediate(function () {
log("never");
});
clearImmediate(id);
Promise.prototype.wait()
同步等待 Promise 结算并直接返回 fulfilled 值;rejected 时原样抛出拒绝值,包括 undefined、null、false、0 和空字符串。
const value = Promise.resolve(42).wait();
log(value); // 42
规则:
- 同一个已结算 Promise 可以重复
wait()。 - 等待期间会让出 ScriptX 运行锁,使其他工作线程和定时器能够完成 Promise。
- 脚本停止会中断尚未完成的等待,不会无限卡住退出。
- 在 ScriptX 主调度器回调中,已结算 Promise 可以
wait();尚未结算的 Promise 会快速抛错,避免主调度器自己等自己造成死锁。 - 页面/主线程异步代码更推荐普通
then(...)/await风格;wait()更适合工作线程或明确需要同步桥接的旧式 API。
sync(func, lock?)
把一个函数包装成“同一时刻只允许一个线程进去执行”的同步函数。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
func | function | 任意函数 | 无 | 要包装的函数,必填 |
lock | any | ReentrantLock 或任意作为锁标识的对象 | 按调用时 this 对象取身份锁 | 可选共享锁 |
返回值
function
说明
- 显式传入
ReentrantLock时直接使用这把 Java 锁。 - 传入其他对象时,ScriptX 按对象身份为它缓存一把
ReentrantLock;多个同步函数传同一个对象就共享锁。 - 没传
lock时,按包装函数被调用时的this对象取身份锁,而不是每次调用临时创建一把互不相关的锁。 - 获取锁使用可中断等待,执行完会在
finally中自动释放。
示例
const lock = threads.lock();
let count = 0;
const addOne = sync(function () {
count += 1;
return count;
}, lock);
threads.start(function () {
log(addOne());
});
threads.start(function () {
log(addOne());
});
disposable.blockedGet(timeoutMs?)
阻塞等待别人调用 setAndNotify(...) 填值。
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
timeoutMs | number | 任意数字 | 0 | 0 表示一直等 |
返回值
any
说明
- 如果在超时前收到了值,就返回那个值。
timeout > 0且到期仍未收到值时,会抛出java.util.concurrent.TimeoutException,不会返回null。timeout < 0会直接抛出参数错误;timeout === 0或不传参数表示一直等待。- 等待过程中会响应脚本暂停和脚本中断。
示例
const box = threads.disposable();
threads.start(function () {
sleep(300);
box.setAndNotify("done");
});
log(box.blockedGet(1000)); // done
disposable.setAndNotify(value)
设置值并唤醒所有正在等待的人。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
value | any | 要投递出去的值 |
返回值
undefined
说明
- 值会先做一次运行时解包,再存进容器。
- 这个容器没有“队列”概念,它只保存一份当前值。
示例
const box = threads.disposable();
box.setAndNotify({ ok: true });
log(JSON.stringify(box.blockedGet()));
disposable.blockedGetOrThrow(exceptionClass, timeout?, defaultValue?)
等待值,并允许把 Java 工作线程等待期间收到的中断转换成指定的 RuntimeException。
只支持两种参数形状:
box.blockedGetOrThrow(java.lang.IllegalStateException);
box.blockedGetOrThrow(java.lang.IllegalStateException, 1000, "timeout-default");
exceptionClass必须是RuntimeException的 Class 对象。在 Rhino 中直接传java.lang.IllegalStateException,不要再写.class。- 只传异常类时会一直等待;等待线程被中断时,会创建并抛出这个运行时异常。
- 传 3 个参数时,超时返回
defaultValue,不会抛TimeoutException。 - 通过
setAndNotify(value)投递的值仍然只会作为普通值返回;即使这个值本身是异常对象,也不会被blockedGetOrThrow(...)自动抛出。 - 参数数量为 2、异常类不是运行时异常、
timeout不是数字时都会抛错。
disposable.toString()
返回 disposable 当前状态的调试字符串,不建议把其文本格式当作稳定协议解析。
atomic.get()
读当前原子值。
const atomic = threads.atomic(3);
log(atomic.get()); // 3
atomic.set(value)
直接把原子值改成 value。
atomic.set(10);
atomic.getAndSet(value)
先返回旧值,再改成新值。
log(atomic.getAndSet(20)); // 旧值
atomic.incrementAndGet()
先加 1,再返回新值。
log(atomic.incrementAndGet());
atomic.decrementAndGet()
先减 1,再返回新值。
log(atomic.decrementAndGet());
atomic.getAndIncrement()
先返回旧值,再加 1。
log(atomic.getAndIncrement());
atomic.getAndDecrement()
先返回旧值,再减 1。
log(atomic.getAndDecrement());
atomic.addAndGet(delta)
把当前值加上 delta,返回新值。
log(atomic.addAndGet(5));
atomic.getAndAdd(delta)
先返回旧值,再加上 delta。
log(atomic.getAndAdd(5));
atomic.compareAndSet(expect, update)
只有当前值等于 expect 时才更新成 update。
返回值
boolean
示例
if (atomic.compareAndSet(10, 99)) {
log("updated");
}
atomic.lazySet(value)
延后可见地设置一个值。
说明
它也是 Java AtomicLong 的原生方法。大多数脚本场景下,如果你不熟并发内存语义,优先用普通 set(...) 就够了。
atomic.lazySet(7);
atomic.toString()
把当前原子值转成字符串。
log(atomic.toString());
lock.lock()
阻塞直到拿到锁。
lock.lock();
try {
log("entered");
} finally {
lock.unlock();
}
lock.unlock()
释放锁。
说明
通常应当放在 finally 里,避免中途抛错导致锁一直不放。
lock.tryLock()
尝试立刻拿锁,不等。
返回值
boolean
if (lock.tryLock()) {
try {
log("got lock");
} finally {
lock.unlock();
}
}
lock.lockInterruptibly()
等待锁,但允许在线程被中断时提前退出等待。
说明
这是 Java 原生锁方法。只有你确实需要“可中断等待锁”时再用;普通脚本更常用 lock()。
lock.newCondition()
基于当前这把锁创建一个条件变量对象。
返回值
condition
示例
const lock = threads.lock();
const condition = lock.newCondition();
lock.isLocked()
判断这把锁当前是否被任何线程持有。
返回值
boolean
log(lock.isLocked());
lock.isHeldByCurrentThread()
判断当前线程是否持有这把锁。
返回值
boolean
log(lock.isHeldByCurrentThread());
condition.await()
释放当前锁并进入等待,直到被唤醒后再重新拿回锁。
说明
- 调用前必须已经持有对应的
lock。 - 这是 Java 条件变量原生语义。
示例
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
condition.awaitNanos(nanosTimeout)
按纳秒超时等待条件。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
nanosTimeout | number | 最多等待多少纳秒 |
返回值
number
Java 语义里它返回剩余纳秒数。
condition.awaitUntil(deadline)
等到某个绝对时间点,或者等到被唤醒。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
deadline | Java Date | 截止时间 |
返回值
boolean
示例
imports("java.util.Date");
lock.lock();
try {
const ok = condition.awaitUntil(new Date(System.currentTimeMillis() + 1000));
log(ok);
} finally {
lock.unlock();
}
condition.signal()
唤醒 1 个正在这个条件上等待的线程。
示例
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
condition.signalAll()
唤醒所有正在这个条件上等待的线程。
示例
lock.lock();
try {
condition.signalAll();
} finally {
lock.unlock();
}
常见报错与原因
回调不是函数
threads.start(action) requires a function
Thread.setTimeout(callback, delay[, ...args]) requires a function callback
Thread.setInterval(callback, delay[, ...args]) requires a function callback
Thread.setImmediate(callback[, ...args]) requires a function callback
setImmediate requires a function callback
sync(func) requires a function
这说明你把必填回调漏掉了,或者传进去的不是函数。
线程还没启动完就挂任务
thread has not started yet
这说明你在线程还没完成启动时就调用了 thread.setTimeout(...)、thread.setInterval(...) 或 thread.setImmediate(...)。
最稳的写法是先 thread.waitFor()。
线程已经结束
thread has already finished
这说明你往一个已经执行完或已经被中断的线程上继续挂任务了。
脚本正在退出
script exiting
这通常说明你在脚本已经准备结束时还试图往主线程继续塞异步任务。
