编辑:
为了在Android环境之外测试此问题,我创建了一个Java应用程序,该应用程序创建一个ExecutorService
,提供一个AttackScript
(相同类)的任务,然后终止。
该正常工作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()");
}
}
原始帖子:
我有一个Android Service
,其中包含一个ExecutorService
字段,负责执行一些任务。
任务是AttackScript
类的对象。我将Future
引用缓存在称为任务的Map<String,Future>
中,以便以后可以取消它们。
Future future = executor.submit(new AttackScript(attack.getWebsite()));
tasks.put(attack.getPushId(), future);
在Service
的{{1}}(用户按下通知按钮时调用)中,我正在取消所有任务
onDestroy()
然后关闭执行器:
private void cancelAllTasks() {
for (Map.Entry<String, Future> futureEntry : tasks.entrySet()) {
futureEntry.getValue().cancel(true);
}
}
最后是 AttackScript 类:
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();
}
}
奇怪的是,很少有任务被中断,并且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);
}
}
}
的执行停止,例如十分之一。但是其他9个任务没有中断,继续在AttackScript
上打开openStreams()。
答案 0 :(得分:4)
不得不寻找替代解决方案,我完全删除了使用线程池,现在实现了单 Thread
s ,存储在Map
中。
再次没有发生中断,因此AtomicBoolean
现在正在控制线程的执行。
private AtomicBoolean stopped = new AtomicBoolean(false);
@Override
public void run() {
while (!stopped.get()) {
readUrl();
}
}
public void stopExecution() {
stopped.set(true);
}
这是一个绝望的举动,但到目前为止唯一有效的举动。
答案 1 :(得分:2)
您已经通过有效的解决方法进行了回答,可以避免此问题,但是我将解释原因。问题不在于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()
来停止请求。