문제

팀원이 다음과 같은 주장을 했습니다.

"Thread.interrupt() 본질적으로 손상되어 있으므로 (거의) 절대 사용해서는 안 됩니다."

나는 이것이 왜 그런지 이해하려고 노력하고 있습니다.

절대 사용하지 않는 것이 알려진 모범 사례입니까? Thread.interrupt()?왜 깨지거나 버그가 있고 강력한 다중 스레드 코드를 작성하는 데 사용하면 안 되는지 증거를 제공할 수 있습니까?

메모 - 디자인 방부제부터 '예쁘다'면 이 질문에는 관심이 없습니다.제 질문은 - 버그가 있나요?

도움이 되었습니까?

해결책

짧은 버전 :

알려진 모범 사례는 Thread.interrupt ()를 사용하지 않아야합니까?

아니.

왜 그것이 깨진 / buggie인지 증거를 제공 할 수 있으며 강력한 멀티 스레드 코드를 작성하는 데 사용해서는 안됩니까?

반대는 사실입니다. 멀티 스레드 코드에 중요합니다.

Listing 7.7 in을 참조하십시오 실제로 Java 동시성 예를 들어.

더 긴 버전 :

여기 주변에서는이 방법을 하나의 특정 장소에서 사용합니다 : 취급 InterruptedExceptions. 그것은 조금 이상하게 보일지 모르지만 여기에 코드에서 보이는 모습은 다음과 같습니다.

try {
    // Some code that might throw an InterruptedException.  
    // Using sleep as an example
    Thread.sleep(10000);
} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    Thread.currentThread().interrupt();
}

이것은 우리를 위해 두 가지를합니다.

  1. 인터럽트 예외를 섭취하지 않습니다. IDE Auto-exception 핸들러는 항상 같은 것을 제공합니다 ie.printStackTrace(); 그리고 황홀한 "Todo : 여기에 가야 할 유용한 것이 필요합니다!" 논평.
  2. 이 방법에서 확인 된 예외를 강요하지 않고 인터럽트 상태를 복원합니다. 구현중인 메소드 서명에 throws InterruptedException 절, 이것은 중단 된 상태를 전파하기위한 다른 옵션입니다.

한 의견 제시자는 체크되지 않은 예외를 사용하여 "스레드를 죽이기 위해"라고 제안했습니다. 이것은 실을 갑자기 죽이는 것이 적절한 일이라는 사전 지식이 있다고 가정합니다. 나는 아니에요.

위에서 인용하기 전에 페이지의 JCIP의 Brian Goetz를 인용합니다.

작업은 특정 중단 정책이있는 서비스 내에서 명시 적으로 실행되도록 설계되지 않는 한 실행 스레드의 중단 정책에 대해 아무것도 가정해서는 안됩니다.

예를 들어, 내가 이것을했다고 상상해보십시오.

} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    // The following is very rude.
    throw new RuntimeException("I think the thread should die immediately", ie);
}

나머지 통화 스택 및 관련 상태의 다른 의무에 관계 없이이 스레드는 지금 죽어야한다고 선언 할 것입니다. 나는 다른 모든 캐치 블록과 상태 정리 코드를 지나서 스레드 죽음으로 곧장 나오려고 노력할 것입니다. 더 나쁜 것은, 나는 스레드의 중단 상태를 소비했을 것입니다. 업스트림 로직은 이제 프로그램 로직 오류가 있는지 또는 모호한 래퍼 내에서 점검 된 예외를 숨기려고하는지 여부를 퍼즐로 만들기 위해 예외를 해체해야합니다.

예를 들어, 팀의 다른 모든 사람들이 즉시해야 할 일이 있습니다.

try {
    callBobsCode();
} catch (RuntimeException e) { // Because Bob is a jerk
    if (e.getCause() instanceOf InterruptedException) {
        // Man, what is that guy's problem?
        interruptCleanlyAndPreserveState();
        // Restoring the interrupt status
        Thread.currentThread().interrupt();
    }
}

중단 된 상태는 특정보다 중요합니다 InterruptException. 특정 예는 왜 Javadoc을 참조하십시오. Thread.interrupt ():

이 스레드가 대기 (), 대기 (long) 또는 대기 (long, int)의 객체 클래스 또는 join (), join (long), join (long, int)의 호출에서 차단 된 경우 (long, int) , 수면 (Long) 또는 수면 (Long, int),이 클래스의 방법은 인터럽트 상태가 지워지고 중단 된 외과를 받게됩니다.

보시다시피, 인터럽트 요청이 처리되지만 인터럽트 상태가 보존 된 경우에만 인터럽트 요청이 처리 될 때 하나 이상의 인터럽트 예고가 생성되고 처리 될 수 있습니다.

다른 팁

내가 알고있는 유일한 방법 Thread.interrupt() 부러졌다 방해하다 그것을 듣는 코드.

그러나 제대로 사용하면 작업 관리 및 취소를위한 좋은 내장 메커니즘처럼 보입니다.

추천합니다 실제로 Java 동시성 그것의 적절하고 안전한 사용에 대한 자세한 내용은 더 많이 읽습니다.

주요 문제 Thread.interrupt() 대부분의 프로그래머는 숨겨진 함정에 대해 알지 못하고 잘못된 방식으로 사용한다는 것입니다. 예를 들어 인터럽트를 처리 할 때 방법이 있습니다. 분명한 깃발 (상태가 손실됩니다).

또한 호출이 항상 스레드를 즉시 방해하지는 않습니다. 예를 들어, 일부 시스템 루틴에 매달려 있으면 아무 일도 일어나지 않을 것입니다. 실제로, 스레드가 플래그를 확인하지 않고 던지는 Java 메소드를 호출하지 않으면 InterruptException, 그러면 방해하는 것은 전혀 영향을 미치지 않습니다.

아니요, 버그가 아닙니다. 실제로 Java의 스레드를 중지하는 방법의 기초입니다. java.util.concurrent의 집행자 프레임 워크에 사용됩니다. java.util.concurrent.FutureTask.Sync.innerCancel.

실패에 관해서는, 나는 그것이 실패한 것을 본 적이 없으며, 그것을 광범위하게 사용했습니다.

언급되지 않은 한 가지 이유는 인터럽트 신호를 잃어 버릴 수있어 스레드를 호출 할 수 있기 때문입니다. 따라서 thread.run () 메소드의 코드가 회전하지 않는 한 thread.interrupt ()를 호출하는 결과는 불확실합니다.

나는 스레드에있을 때 그것을 발견했습니다 ONE 나는 처형한다 DriverManager.getConnection() 사용 가능한 데이터베이스 연결이 없을 때(예: 서버가 다운되어 마침내 이 줄이 발생합니다) SQLException) 그리고 스레드에서 TWO 나는 분명히 전화한다. ONE.interrupt(), 그러면 둘 다 ONE.interrupted() 그리고 ONE.isInterrupted() 반품 false 첫 번째 줄에 배치하더라도 catch{} 어디를 차단해 SQLException 처리됩니다.

물론 나는 추가 세마포어를 구현하여 이 문제를 해결했지만 이는 매우 골치 아픈 문제입니다. 15년 간의 Java 개발에서 처음으로 발생하는 문제이기 때문입니다.

버그인 때문인거 같은데 com.microsoft.sqlserver.jdbc.SQLServerDriver.그리고 나는 전화가 native 함수는 자체 예외를 처리하는 모든 경우에 이 예외를 소비하지만 성공하면 이를 유지합니다.

토멕

추신나는 찾았다 유사한 문제.

pps 나는 위에 쓰는 내용에 대한 매우 짧은 예를 둘입니다.등록된 수업은 다음에서 확인할 수 있습니다. sqljdbc42.jar.2015-08-20에 빌드된 클래스에서 이 버그를 발견한 후 사용 가능한 최신 버전(2017-01-12)으로 업데이트했는데 버그가 여전히 존재합니다.

import java.sql.*;

public class TEST implements Runnable{
    static{
        try{
//register the proper driver
           Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        }
        catch(ClassNotFoundException e){
            System.err.println("Cannot load JDBC driver (not found in CLASSPATH).");
        }
    }

    public void run() {
        Thread.currentThread().interrupt();
        System.out.println(Thread.currentThread().isInterrupted());
//prints true
        try{
            Connection conn = DriverManager.getConnection("jdbc:sqlserver://xxxx\\xxxx;databaseName=xxxx;integratedSecurity=true");
        }
        catch (SQLException e){
            System.out.println(e.getMessage());
        }
        System.out.println(Thread.currentThread().isInterrupted());
//prints false
        System.exit(0);
    }

    public static void main(String[] args){
        (new Thread(new TEST())).start();
    }
}

다음과 같이 완전히 잘못된 것을 전달하면 "foo", 로 DriverManager.getConnection(), "foo에 적합한 드라이버를 찾을 수 없습니다"라는 메시지가 표시되고 두 번째 출력은 예상한 대로 여전히 true입니다.그러나 올바르게 작성된 문자열을 전달했지만 서버가 다운되었거나 네트워크 연결이 끊어진 경우(일반적으로 프로덕션 환경에서 발생할 수 있음) java.net 소켓 시간 초과 오류 출력 및 스레드의 interrupted() 상태가 손실되었습니다.

문제는 구현이 버기가 아니라 스레드가 방해 될 때 알려지지 않은 상태에 있다는 것이 아니라 예측할 수없는 행동으로 이어질 수 있다는 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top