Java 5 : java.util.concurrent.futuretask- 취소 () 및 done ()의 의미론
-
22-07-2019 - |
문제
나는 현재 Futuretasks와 Executors를 사용하여 멀티 스레드 환경에서 불쾌한 버그를 사냥하고 있습니다. 기본 아이디어는 고정 된 수의 스레드가 AA 테이블에 표시 될 결과를 계산하는 개별 FutureTask를 실행하도록하는 것입니다 (GUI 측면을 여기에 신경 쓰지 마십시오).
나는 이것을 오랫동안보고 있었다. 나는 내 정신을 의심하기 시작했다.
이 코드를 고려하십시오.
public class MyTask extends FutureTask<Result> {
private String cellId;
...
protected void done() {
if (isCancelled()) return;
try {
Result r = get(); // should not wait, because we are done
... // some processing with r
sendMessage(cellId, r);
} catch (ExecutionException e) { // thrown from get
...
} catch (InterruptedException e) { // thrown from get
...
}
}
...
}
언제 done()
Mytask 인스턴스를 처리하는 집행자가 호출합니다. 작업이 취소되었으므로 도착했는지 확인합니다. 그렇다면 나머지 활동을 모두 건너 뜁니다. 특히 전화하지 않습니다. sendMessage()
.
futuretask.done ()에 대한 문서는 다음과 같이 말합니다.
이 작업이 상태 ISDON (일반적으로 또는 취소를 통해)으로 전환 할 때 보호 된 방법이 호출되었습니다. 기본 구현은 아무것도하지 않습니다. 서브 클래스는이 메소드를 무시하여 완료 콜백을 호출하거나 부기를 수행 할 수 있습니다. 이 방법의 구현 내에서 상태를 쿼리 하여이 작업이 취소되었는지 여부를 결정할 수 있습니다. (API 참조)
그러나 내가 문서에서 얻지 못하는 것 FutureTask
의미론입니다 동안 done()
실행 중입니다. 내가 통과한다면 어떨까요? isCancelled()
처음에 확인하지만 바로 그 직후에 다른 스레드는 cancel()
방법? 내 임무가 마음을 바꾸고 대답하게 할 것인가 isCancelled() == true
그때부터?
그렇다면 메시지가 전송되었는지 나중에 어떻게 알 수 있습니까? 보고 있습니다 isDone()
작업 실행이 완료되었다고 말하지만 isCancelled()
그때도 사실이었고, 메시지를 제 시간에 보내야하는지 알 수 없었습니다.
어쩌면 이것은 분명하지만 지금은 실제로 그것을 보지 못합니다.
해결책
API에서 (강조 광산) :
공개 부울 취소 (부울 Mayinterruptifrunning)
인터페이스에서 복사 한 설명 : 미래
이 작업의 실행을 취소하려고 시도합니다. 작업이 이미 완료되면이 시도가 실패합니다., 이미 취소되었거나 다른 이유로 취소 할 수 없습니다.
따라서 FutureTask는 ISDONE 단계로 전환했을 때 작업을 취소 할 수 없다는 가정을 제외하고 있습니다.
다른 팁
FutureTask#done()
주어진 인스턴스에 대해 한 번 이상이라고 불리며 한 가지 이유 만 요구됩니다. run()
오류의 유무에 관계없이 완료되었습니다 cancel()
앞의 이벤트 중 하나가 발생하기 전에 실행되었습니다. 이러한 결과 중 하나에 의한 완료 기록은 다음과 같습니다. 래칭. 이유 a FutureTask
"동시에"경쟁하는 이벤트에 관계없이 완성 된 것은 변경 될 수 없습니다.
따라서 내부 FutureTask#done()
중 하나만 isCancelled()
또는 isDone()
그때와 영원히 더 많은 것을 돌려 줄 것입니다. 구별하기가 어렵습니다 isDone()
오류 또는 성공적인 완료 방식으로 True보고. 당신은 무시할 수 없습니다 set()
또는 setException(Throwable)
결정적으로, 둘 다 내부 대표로 AQS
여부를 결정합니다 시도 값의 성공적인 수확량을 기록하거나 예외를 만나면 예외가 발생해야합니다. 두 가지 방법을 우선적으로 사용하면 호출되었음을 알 수 있지만 기본 구현의 결정을 관찰 할 수는 없습니다. 두 이벤트 중 하나가 "너무 늦게"발생하는 경우 —SAY, ~ 후에 취소 - 값을 기록하려는 시도 또는 예외는 무시됩니다.
구현을 연구하면서, 오류로부터 불확실한 성공적인 결과를 식별하는 유일한 방법은 총알을 물고 전화하는 것입니다. get()
.
결과를 바탕으로 작업의 "외부"메시지를 보내지 않겠습니까? 미래u003CV> 객체가 반환했습니다 ExecutorService? 이 패턴을 사용했는데 잘 작동하는 것 같습니다. 호출 가능u003CV> an을 통한 작업 ExecutorService. 그런 다음 각 기본 작업에 대해 대기하는 보조 작업을 제출하십시오. 미래u003CV> 기본 작업 중에서 미래u003CV> 기본 작업이 성공적으로 완료되었음을 나타냅니다. 이 접근법에는 추측이 없습니다. 전화 할 때 미래u003CV> .가져 오기() 반품, 당신은 버전을 호출하지 않는 한 작업이 터미널 상태에 도달했음을 보장합니다. 가져 오기 시간 초과 논쟁이 필요합니다.
이 접근법을 취하면 두 개의 별도를 사용해야합니다. ExecutorService 인스턴스 : 하나는 1 차 작업과 2 차 작업을위한 것입니다. 이것은 교착 상태를 방지하기위한 것입니다. 2 차 작업이 시작되어 스레드 풀 크기가 제한 될 때 시작부터 1 차 작업을 시작하는 것을 원하지 않습니다.
확장 할 필요가 없습니다 FutureTasku003CV> 조금도. 작업을 그대로 구현하십시오 호출 가능u003CV> 사물. 그러나 어떤 이유로 어떤 이유로 작업이 취소되었는지 감지하려는 경우 호출 가능u003CV> 코드, 스레드의 인터럽트 상태를 확인하십시오. Thread.terrupted ().
나는 당신이 전화 할 수있는 작은 테스트 케이스를 작성하는 것이 좋습니다. cancel()
당신의 동안 Future
인스턴스가 매달려 있습니다 done()
그리고 무슨 일이 일어나는지보십시오.