无法中断 ExecutorService 的任务
Can't interrupt tasks of ExecutorService
编辑:
为了在 Android 环境之外测试这个问题,我创建了一个 Java 应用程序,它创建了一个 ExecutorService
,提供了一个 AttackScript
的任务(相同 class) 然后终止。
这按预期工作 100%,线程被中断,任务停止。
您甚至不必通过 Future.cancel(true)
取消任务。 ExecutorService.shutdownNow()
完成任务。 Android 的 Service
中有什么东西以某种方式扰乱了线程池吗?
按预期工作的代码:
public static void main(String[] args) {
AttackScript script = new AttackScript("http://ninjaflex.com/");
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(script);
executor.submit(script);
executor.submit(script);
executor.submit(script);
sleep(1300);
// Automatically interrupts threads in the pool.
executor.shutdownNow();
}
private static void sleep(long timeMilli){
try {
Thread.sleep(timeMilli);
} catch(Exception e) {
System.out.println("Error sleep()");
}
}
原post:
我有一个 Android Service
,其中包含一个 ExecutorService
字段,负责 运行 一些任务。
任务是 AttackScript
class 的对象。我将 Future
引用缓存在一个名为任务的 Map<String,Future>
中,这样我以后就可以取消它们。
Future future = executor.submit(new AttackScript(attack.getWebsite()));
tasks.put(attack.getPushId(), future);
在Service
的onDestroy()
(当用户按下通知按钮时调用)我正在取消所有任务
private void cancelAllTasks() {
for (Map.Entry<String, Future> futureEntry : tasks.entrySet()) {
futureEntry.getValue().cancel(true);
}
}
然后关闭执行器:
private void shutdownThreadPool() {
// https://www.baeldung.com/java-executor-service-tutorial
executor.shutdown();
try {
if (executor.awaitTermination(800, TimeUnit.MILLISECONDS))
executor.shutdownNow();
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
最后是 AttackScript class:
public class AttackScript implements Runnable {
private static final String TAG = "AttackScript";
private URL url;
public AttackScript(String website) {
initializeUrl(website);
}
private void initializeUrl(String website) {
try {
url = new URL(website);
} catch (MalformedURLException e) {
Log.e(TAG, "Wrong url?", e);
}
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
readUrl();
}
Log.d(TAG, "Stopped requesting from " + url + " server.");
}
private void readUrl() {
InputStream in = null;
try {
in = url.openStream();
} catch (IOException e) {
Log.e(TAG, "openStream() error.", e);
} finally {
closeInputStream(in);
}
}
private void closeInputStream(InputStream in) {
try {
in.close();
Log.d(TAG, "InputStream closed for " + url);
} catch (IOException e) {
Log.e(TAG, "Error while closing the input stream.", e);
}
}
}
奇怪的是,很少有十分之一的任务被中断并且 AttackScript
的执行停止。但是其他 9 个任务没有被打断,在 URL
s.
上继续 openStreams()
被迫寻找替代解决方案我完全删除 线程池的使用,现在实施单 Thread
s,存储在Map
.
再次中断从未发生,因此 AtomicBoolean
现在正在控制线程的执行。
private AtomicBoolean stopped = new AtomicBoolean(false);
@Override
public void run() {
while (!stopped.get()) {
readUrl();
}
}
public void stopExecution() {
stopped.set(true);
}
这是一个孤注一掷的举动,但迄今为止唯一有效的举动。
您已经回答了一个有效的解决方法来避免这个问题,但我会解释原因。故障不在于 ExecutorService
,而在于线程的中断状态被网络库静默清除。
正如您和另一位评论者所发现的,这很可能取决于您使用的特定设备及其 Android 版本。
从 Android 4.4 开始,OkHttp
用作 HttpUrlConnection
。每个线程何时中断与 InputStream
在旧版本中是否已关闭之间存在竞争条件。
作为 close()
调用的一部分,这段代码最终被执行:
public void throwIfReached() throws IOException {
if (Thread.interrupted()) {
throw new InterruptedIOException("thread interrupted");
}
if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) {
throw new InterruptedIOException("deadline reached");
}
}
你可以看到基于Thread.interrupted()
调用它清除了线程的中断状态并且再也没有设置它。
更糟糕的是,您似乎可以改为依赖 InterruptedIOException
,但在关闭流时会在内部静默处理,因此您没有机会处理它。
当我使用更新版本的 OkHttp
时,您的代码示例对我有用。在以后的版本中,看起来更注意保持中断状态并且它实际上按预期工作。
但是,根据一些搜索,从历史上看,中断似乎不能很好地与 OkHttp
配合使用,为了停止请求,他们建议尽可能使用 Call.cancel()
。
编辑:
为了在 Android 环境之外测试这个问题,我创建了一个 Java 应用程序,它创建了一个 ExecutorService
,提供了一个 AttackScript
的任务(相同 class) 然后终止。
这按预期工作 100%,线程被中断,任务停止。
您甚至不必通过 Future.cancel(true)
取消任务。 ExecutorService.shutdownNow()
完成任务。 Android 的 Service
中有什么东西以某种方式扰乱了线程池吗?
按预期工作的代码:
public static void main(String[] args) {
AttackScript script = new AttackScript("http://ninjaflex.com/");
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(script);
executor.submit(script);
executor.submit(script);
executor.submit(script);
sleep(1300);
// Automatically interrupts threads in the pool.
executor.shutdownNow();
}
private static void sleep(long timeMilli){
try {
Thread.sleep(timeMilli);
} catch(Exception e) {
System.out.println("Error sleep()");
}
}
原post:
我有一个 Android Service
,其中包含一个 ExecutorService
字段,负责 运行 一些任务。
任务是 AttackScript
class 的对象。我将 Future
引用缓存在一个名为任务的 Map<String,Future>
中,这样我以后就可以取消它们。
Future future = executor.submit(new AttackScript(attack.getWebsite()));
tasks.put(attack.getPushId(), future);
在Service
的onDestroy()
(当用户按下通知按钮时调用)我正在取消所有任务
private void cancelAllTasks() {
for (Map.Entry<String, Future> futureEntry : tasks.entrySet()) {
futureEntry.getValue().cancel(true);
}
}
然后关闭执行器:
private void shutdownThreadPool() {
// https://www.baeldung.com/java-executor-service-tutorial
executor.shutdown();
try {
if (executor.awaitTermination(800, TimeUnit.MILLISECONDS))
executor.shutdownNow();
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
最后是 AttackScript class:
public class AttackScript implements Runnable {
private static final String TAG = "AttackScript";
private URL url;
public AttackScript(String website) {
initializeUrl(website);
}
private void initializeUrl(String website) {
try {
url = new URL(website);
} catch (MalformedURLException e) {
Log.e(TAG, "Wrong url?", e);
}
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
readUrl();
}
Log.d(TAG, "Stopped requesting from " + url + " server.");
}
private void readUrl() {
InputStream in = null;
try {
in = url.openStream();
} catch (IOException e) {
Log.e(TAG, "openStream() error.", e);
} finally {
closeInputStream(in);
}
}
private void closeInputStream(InputStream in) {
try {
in.close();
Log.d(TAG, "InputStream closed for " + url);
} catch (IOException e) {
Log.e(TAG, "Error while closing the input stream.", e);
}
}
}
奇怪的是,很少有十分之一的任务被中断并且 AttackScript
的执行停止。但是其他 9 个任务没有被打断,在 URL
s.
被迫寻找替代解决方案我完全删除 线程池的使用,现在实施单 Thread
s,存储在Map
.
再次中断从未发生,因此 AtomicBoolean
现在正在控制线程的执行。
private AtomicBoolean stopped = new AtomicBoolean(false);
@Override
public void run() {
while (!stopped.get()) {
readUrl();
}
}
public void stopExecution() {
stopped.set(true);
}
这是一个孤注一掷的举动,但迄今为止唯一有效的举动。
您已经回答了一个有效的解决方法来避免这个问题,但我会解释原因。故障不在于 ExecutorService
,而在于线程的中断状态被网络库静默清除。
正如您和另一位评论者所发现的,这很可能取决于您使用的特定设备及其 Android 版本。
从 Android 4.4 开始,OkHttp
用作 HttpUrlConnection
。每个线程何时中断与 InputStream
在旧版本中是否已关闭之间存在竞争条件。
作为 close()
调用的一部分,这段代码最终被执行:
public void throwIfReached() throws IOException {
if (Thread.interrupted()) {
throw new InterruptedIOException("thread interrupted");
}
if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) {
throw new InterruptedIOException("deadline reached");
}
}
你可以看到基于Thread.interrupted()
调用它清除了线程的中断状态并且再也没有设置它。
更糟糕的是,您似乎可以改为依赖 InterruptedIOException
,但在关闭流时会在内部静默处理,因此您没有机会处理它。
当我使用更新版本的 OkHttp
时,您的代码示例对我有用。在以后的版本中,看起来更注意保持中断状态并且它实际上按预期工作。
但是,根据一些搜索,从历史上看,中断似乎不能很好地与 OkHttp
配合使用,为了停止请求,他们建议尽可能使用 Call.cancel()
。