Java 스레드 중단의 본질 깊이 이해

1. Java가 중단되는 현상 우선 Thread 클래스의 몇 가지 방법을 살펴보자.
public static boolean interrupted
현재 라인이 중단되었는지 테스트합니다.라인의 중단 상태는 이 방법으로 제거됩니다.다시 말하면 이 방법을 두 번 연속 호출하면 두 번째 호출은false로 되돌아옵니다. 첫 번째 호출이 중단된 상태를 제거한 후, 두 번째 호출이 중단된 상태를 검사하기 전에 현재 라인이 다시 중단된 상황을 제외합니다.
public boolean isInterrupted()
라인이 중단되었는지 테스트합니다.라인의 중단 상태는 이 방법의 영향을 받지 않는다.
public void interrupt()
라인을 끊다.중단과 관련된 몇 가지 방법과 행동을 열거하였으며,interrupt가 중단 라인임을 볼 수 있습니다.만약 자바의 인터럽트 메커니즘을 이해하지 못한다면, 이러한 해석은 오해를 일으키기 쉬우며, 루트를 호출한interrupt 방법은 반드시 루트를 중단할 것이라고 생각한다.사실 Java의 중단은 일종의 협력 메커니즘이다.즉, 라인 대상을 호출하는interrupt 방법이 반드시 실행 중인 라인을 중단하는 것은 아니다. 라인 자체가 적당한 시기에 자신을 중단하도록 요구할 뿐이다.모든 스레드에는 boolean의 인터럽트 상태가 있습니다. (반드시 대상의 속성이 아니라 사실상, 이 상태는Thread의 필드가 아닙니다.) 인터럽트 방법은 이 상태를true
 
public class TestInterrupt {
public static void main(String[] args) {
Thread t = new MyThread();
t.start();
t.interrupt();
System.out.println(" interrupt ");
}
static class MyThread extends Thread {
public void run() {
int num = longTimeRunningNonInterruptMethod(2, 0);
System.out.println(" ,num=" + num);
System.out.println(" :" + Thread.interrupted());
}
private static int longTimeRunningNonInterruptMethod(int count, int initNum) {
for(int i=0; i<count; i++) {
for(int j=0; j<Integer.MAX_VALUE; j++) {
initNum ++;
}
}
return initNum;
}
}
}
로 설정할 뿐입니다. 일반적인 상황에서 다음과 같은 내용을 출력합니다. 호출된interrupt 방법은 장시간 작업이 끝나고num=-2 스레드의 인터럽트 상태:true를 볼 수 있으며,interrupt 방법은 반드시 라인을 중단할 수 없습니다.하지만 다음 절차로 바뀌면 상황은 어떨까?
 
import java.util.concurrent.TimeUnit;
public class TestInterrupt {
public static void main(String[] args) {
Thread t = new MyThread();
t.start();
t.interrupt();
System.out.println(" interrupt ");
}
static class MyThread extends Thread {
public void run() {
int num = -1;
try {
num = longTimeRunningInterruptMethod(2, 0);
} catch (InterruptedException e) {
System.out.println(" ");
throw new RuntimeException(e);
}
System.out.println(" ,num=" + num);
System.out.println(" :" + Thread.interrupted());
}
private static int longTimeRunningInterruptMethod(int count, int initNum) throws InterruptedException{
for(int i=0; i<count; i++) {
TimeUnit.SECONDS.sleep(5);
}
return initNum;
}
}
}
실행을 통해 알 수 있듯이 프로그램이 비정상적으로 정지되었고run 방법의 두 개의 인쇄 문장이 실행되지 않았습니다.그럼, 차이는 어디에 있습니까?일반적으로 말하면 만약에 하나의 방법이 인터럽트 Exception을 던진다고 성명하면 이 방법은 중단할 수 있음을 나타낸다. (방법에서 중단을 처리하지 않았는데도 인터럽트 Exception을 던진다고 성명한 것을 제외하고) 즉, 중단 가능한 방법은interrupt 호출에 응답한다. (예를 들어 sleep 응답 인터럽트의 작업은 중단 상태를 제거하고 인터럽트 Exception을 던진다.) 인터럽트 호출이 중단 가능한 방법 이전에 호출된다면,인터럽트 방법은 반드시 인터럽트를 처리합니다. 위의 예와 같이 인터럽트 방법은 run이 sleep에 들어가지 않았을 때 호출될 수 있지만 sleep가 인터럽트를 감지하면 인터럽트를 처리합니다.만약 중단 가능한 방법이 실행 중일 때interrupt를 호출한다면 어떻게 될까요?이것은 인터럽트 방법이 인터럽트를 처리하는 시기를 보아야 한다. 인터럽트 방법이 인터럽트 상태가true임을 감지할 수 있다면 인터럽트를 처리해야 한다.우리가 시작하는 코드에 인터럽트 처리를 추가합시다.그러면 사용자 정의 인터럽트 방법은 어떻게 인터럽트를 처리해야 합니까?그것은 바로 처리 중단에 적합한 곳에서 라인 중단 상태를 측정하고 처리하는 것이다.
 
public class TestInterrupt {
public static void main(String[] args) throws Exception {
Thread t = new MyThread();
t.start();
// TimeUnit.SECONDS.sleep(1);// ,
t.interrupt();
System.out.println(" interrupt ");
}
static class MyThread extends Thread {
public void run() {
int num;
try {
num = longTimeRunningNonInterruptMethod(2, 0);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(" ,num=" + num);
System.out.println(" :" + Thread.interrupted());
}
private static int longTimeRunningNonInterruptMethod(int count, int initNum) throws InterruptedException {
if(interrupted()) {
throw new InterruptedException(" ");
}
for(int i=0; i<count; i++) {
for(int j=0; j<Integer.MAX_VALUE; j++) {
initNum ++;
}
//
if(interrupted()) {
// ,
throw new InterruptedException(" ");
}
}
return initNum;
}
}
}
위의 코드와 같이 방법longTimeRunningMethod는 현재 이미 중단할 수 있는 방법이다.방법에 들어갈 때 요청이 중단되었는지 판단하고 만약 그렇다면 상응하는 처리를 하지 않는다.처리 과정에서 적당한 곳에서 처리가 중단될 수도 있다. 예를 들어 위의 가장 안쪽 순환이 끝난 후.이 코드에서 인터럽트 검출은Thread의 정적 방법인interrupted를 사용합니다. 인터럽트 상태를false로 설정하고 이전 상태를 되돌려줍니다. isInterrupted는 인터럽트 검출일 뿐 인터럽트 상태를 바꾸지 않습니다.일반적으로 중단 요청을 처리했으면 상태를false로 설정해야 합니다.하지만 구체적으로 실제 상황을 봐야 한다.둘째, Java 중단의 본질은 역사적으로 Java가 선점식 제한 중단을 제공하려고 했지만 문제가 많았다. 예를 들어 폐기된 Thread이다.stop、Thread.suspend 및 Thread.resume 등.다른 한편, 자바 응용 코드의 건장성을 고려하여 프로그래밍 문턱을 낮추고 밑바닥 메커니즘을 잘 모르는 프로그래머가 시스템을 파괴하지 않을 확률을 줄였다.현재 Java의 스레드 스케줄링은 선점식 인터럽트를 제공하지 않고 협업식 인터럽트를 사용합니다.사실 협동식의 중단은 원리가 매우 간단하다. 바로 어떤 중단을 나타내는 표기를 윤문하는 것이다. 우리는 어떤 일반 코드에서도 실현할 수 있다.예를 들어 아래의 코드:
 
volatile bool isInterrupted;
//…
while(!isInterrupted) {
compute();
}
그러나 상술한 코드 문제도 매우 뚜렷하다.컴퓨터의 실행 시간이 비교적 길면 중단이 제때에 응답되지 않습니다.다른 한편, 윤문 검사 표지 변수의 방식을 이용하여wait와sleep 등 라인 차단 작업을 중단하려는 것도 속수무책이다.만약에 상기 사고방식을 계속 이용한다면 중단이 제때에 응답되도록 하려면 반드시 가상 기기의 밑바닥에서 라인 스케줄링을 하고 표기 변수를 검사해야 한다.예, JVM에서는 그렇게 하는 것이 맞습니다.다음은 자바에서 발췌한 것이다.lang.Thread의 소스 코드:
 
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
//…
private native boolean isInterrupted(boolean ClearInterrupted);
isInterrupted는 네이티브 방법으로 선언되었고 JVM 하부의 실현에 달려 있음을 알 수 있습니다.실제로 JVM 내부에서는 각 스레드에 대해 인터럽트 태그가 유지되고 있습니다.그러나 응용 프로그램은 이 인터럽트 변수에 직접 접근할 수 없습니다. 다음 몇 가지 방법을 통해 조작해야 합니다.
 
public class Thread {
//
public void interrupt() { ... }
//
public boolean isInterrupted() { ... }
// ,
public static boolean interrupted() { ... }
...
}
일반적인 상황에서 라인을 호출하는interrupt 방법은 즉각 인터럽트를 일으킬 수 없습니다. 단지 JVM 내부의 인터럽트 표시만 설정되어 있습니다.따라서 인터럽트 표시를 검사함으로써 프로그램은 특수한 조작을 할 수도 있고 인터럽트를 완전히 무시할 수도 있다.만약 JVM이 이런 초라한 인터럽트 메커니즘만 제공한다면 응용 프로그램이 인터럽트 변수를 정의하고 윤문하는 방법에 비해 기본적으로 아무런 장점이 없다고 생각할 수 있다.JVM 내부 인터럽트 변수의 주요 이점은 일부 상황에 대해 자동 인터럽트 빠져들기를 시뮬레이션하는 메커니즘을 제공하는 것이다.라인 스케줄링과 관련된 차단 호출을 실행할 때 (예를 들어wait,sleep,join) 중단이 발생하면 차단된 라인은 가능한 한 빨리 Interrupted Exception을 던집니다.따라서 우리는 아래의 코드 프레임워크로 스레드 차단 인터럽트를 처리할 수 있다.
 
try {
//wait、sleep join
}
catch(InterruptedException e) {
//
}
소위 "가능한 한 빨리"라고 하는 것은 JVM이 스레드 스케줄링의 간극에서 인터럽트 변수를 검사하는 것이다. 속도는 JVM의 실현과 하드웨어의 성능에 달려 있다고 나는 추측한다.3. 일부 루트 차단 작업은 Interrupted Exception을 내보내지 않지만, 일부 루트 차단 작업은 JVM이 자동으로 Interrupted Exception 이상을 내보내지 않습니다.예를 들어, 일부 입출력 작업과 내부 잠금 작업 등이 있습니다.이런 조작에 대해서는 다른 방식으로 중단을 시뮬레이션할 수 있다. 1)java.io의 비동기식 socket I/O가 socket을 읽을 때 InputStream과 OutputStream의read와 write 방법은 기다림을 막지만 자바 중단에 응답하지 않습니다.단, Socket의close 방법을 호출하면 막힌 라인에서 SocketException 이상이 발생합니다.2) Selector를 이용한 비동기식 I/O 스레드가 Selector에 막히면select(java.nio.channels에서),wakeup 방법을 호출하면 ClosedSelectorException 이상을 일으킬 수 있습니다.3) 자물쇠 가져오기 라인이 내부 자물쇠를 가져오기를 기다리고 있다면 중단할 수 없습니다.그러나 Lock류의lockInterruptibly 방법을 이용하여 우리는 자물쇠를 기다리는 동시에 중단 능력을 제공할 수 있다.4. 두 가지 프로그래밍 원칙 외에 임무와 루틴이 분리된 프레임워크에서 임무는 자신이 어느 루틴에 호출될지 모르고 루틴 처리가 중단된 전략도 모른다.따라서 작업이 라인 인터럽트 표시를 설정한 후에 작업이 취소될 것을 보장할 수 없습니다.따라서 다음과 같은 두 가지 프로그래밍 원칙이 있다. 1) 라인의 중단 정책을 알지 않으면 중단해서는 안 된다.이 원칙은 Executer 같은 프레임워크의 인터럽트 방법을 직접 호출하지 말고 Future와 같은 방법을 이용해야 한다는 것을 알려준다.cancel 방법으로 작업을 취소합니다.2) 작업 코드는 실행 루틴에 대한 중단의 의미를 추측해서는 안 된다.이 원칙은 일반적인 코드가 Interrupted Exception 이상에 부딪혔을 때, 그것을 포획한 후에 삼켜서는 안 되고, 상부 코드에 계속 던져야 한다는 것을 알려준다.한 마디로 하면 자바의 비점유식 중단 메커니즘은 우리가 전통적인 점유식 중단 사고방식을 바꾸고 그 본질을 이해하는 토대에서 상응하는 원칙과 모델을 이용하여 프로그래밍해야 한다고 요구한다.

좋은 웹페이지 즐겨찾기