Java에서 스레드를 어떻게 죽이나 요?


374

java.lang.ThreadJava에서를 어떻게 죽 입니까?


2
지금까지는 스레드를 죽일 수 없습니다. dead-lock prone으로 인해 destroy ()가 구현되지 않았기 때문에
AZ_

1
ExecutorStatus이 질문에 대한 답변을 선호합니다 : stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Kirby

9
@loungerdork "잠금 및 기타 함정의 손실에도 불구하고 Java가 제어 할 수없는 런 어웨이 스레드에 대해 안전 중지 / 파괴 방법을 구현해야한다고 생각합니다. 따라서 안전하지 않은 스레드 중지 를 원합니다 . 이미 가지고 있다고 생각합니다.
DJClayworth

4
2009 년에 어떤 종류의 질문이 212 개의 공감대를 받게 될지는 놀라운 일입니다. 이것은 오늘날 즉시 파괴 될 것입니다.
Jonathon Reinhart

7
@ JonathonReinhart : 왜 그렇습니까? 요즘에도 합법적 인 질문 인 것 같습니다. 어쩌면 런 어웨이 스레드가있을 때 발생하는 좌절감을 모르고 더 이상 사용되지 않는 함수 만 사용하여이를 처리 할 수 ​​있습니까?
TFuto

답변:


189

더 이상 사용되지 않는 이유는 SunThread.stop() 의이 스레드를 참조하십시오 . 왜 이것이 나쁜 방법인지, 그리고 일반적으로 스레드를 안전하게 중지하기 위해 수행해야 할 사항에 대해 자세히 설명합니다.

그들이 권장하는 방법은 공유 변수를 플래그로 사용하여 백그라운드 스레드를 중지하도록 요청하는 것입니다. 그런 다음 스레드 종료를 요청하는 다른 객체로이 변수를 설정할 수 있습니다.


1
isAlive ()를 중단 한 스레드를 확인하면 true를 반환하고 현재 ThreadGroup []에 계속 추가됩니다. Thread.currentThread.getThreadGroup (). list (); 스레드의 모든 스레드를 인쇄하고 흐름을 반복하면 스레드의 여러 인스턴스가 표시됩니다.
AZ_

3
PC에 있다면 아무런 문제가 없지만 모바일 용 소프트웨어를 개발하고 있다면 (Android에서 경험
했음)

2
플래그를 통해 중지 요청의 즉각적인 통신을 보장하기 위해 권장 사항에 명시된대로 변수가 일시적이어야합니다 (또는 변수에 대한 액세스가 동기화되어야 함).
mtsz 2016 년

2
이 시점에서 해당 링크가 종료되었습니다. 하지만 archive.org에서 찾을 수있었습니다. web.archive.org/web/20090202093154/http://java.sun.com/j2se/…
Jay Taylor

2
나는 방법을 사용한다 getConnection()java.sql.DriverManager 합니다. 연결 시도가 너무 오래 걸리면 호출하여 해당 스레드를 종료하려고 시도 Thread.interrupt()하지만 스레드에 전혀 영향을 미치지 않습니다. 는 Thread.stop()오라클이 경우 작동하지한다고 말했습니다 있지만, 그러나 작동 interrupt()하지 않습니다. 어떻게 작동하는지 궁금하고 더 이상 사용되지 않는 방법을 사용하지 마십시오.
Danny Lo

129

일반적으로 ..

Thread.interrupt ()를 사용하여 수행중인 작업을 중단하도록 요청합니다 (javadoc link)

왜 javadoc에 있는지에 대한 좋은 설명 (java technote link)


@Fredrik interrupt()메소드가 호출 될 때 스레드 컨텍스트는 어떻게됩니까 ? 주요 질문은 각각의 새 스레드에 대한 로그 생성 과 관련 있습니다.
ABcDexter

3
@ABcDexter 요점은 인터럽트가 아무 것도 방해하지 않는다는 것입니다. 누군가가 무엇이든간에 인터럽트하도록 요청한 스레드의 코드 (또는 스레드가 호출하는 코드)에 신호를 보냅니다. 그런 다음 스레드는 처리를 중단하고 반환해야합니다. 마치 마치 마치 수행해야하는 것처럼 (그리고 그 시점에서 스레드 컨텍스트도 무시 될 수 있습니다). OTOH, 스레드를 강제로 중지했다면 질문이 정말 좋을 것이고 대답은 정의되지 않았습니다.
Fredrik

64

Java 스레드에서는 종료되지 않지만 스레드 중지는 협업 방식으로 수행됩니다 . 스레드를 종료하라는 요청을 받으면 스레드가 정상적으로 종료 될 수 있습니다.

volatile boolean스레드가 해당 값으로 설정 될 때 스레드가 주기적으로 확인하고 종료 하는 필드가 종종 사용됩니다.

나는 하지 않을 를 사용하는 boolean스레드가해야하는지 여부를 확인하기 위해 종료 . volatile필드 수정 자로 사용하면 안정적으로 작동하지만 코드가 더 복잡해지면 while루프 내에서 다른 차단 방법을 사용 하기 때문에 코드가 전혀 종료되지 않거나 적어도 시간이 오래 걸릴 수 있습니다 아마도 원한다.

특정 차단 라이브러리 방법은 중단을 지원합니다.

모든 스레드에는 이미 부울 플래그 중단 상태 가 있으므로 이를 사용해야합니다. 다음과 같이 구현할 수 있습니다.

public void run() {
   try {
      while (!interrupted()) {
         // ...
      }
   } catch (InterruptedException consumed)
      /* Allow thread to exit */
   }
}

public void cancel() { interrupt(); }

실제로 Java Concurrency에서 채택한 소스 코드 . cancel()메소드가 공용 이므로 다른 스레드가 원하는대로이 메소드를 호출 할 수 있습니다.


신뢰할 수없는 코드를 플러그인 또는 스크립트로 실행하면 어떻게해야합니까? Java에는 신뢰할 수없는 코드를위한 샌드 박스가 내장되어 있습니다. 그리고 그 샌드 박스는 쓸모가 없으며, 강력한 정지없이 작동 할 수 있습니다. Java로 브라우저를 작성한다고 가정하십시오. 임의의 페이지 스크립트를 죽이는 기능은 매우 가치가 있습니다.
ayvango

@ayvango 그런 다음 자신의 샌드 박스에서 해당 스크립트를 실행해야합니다. Java 샌드 박스는 응용 프로그램의 일부가 아니라 응용 프로그램으로부터 시스템을 보호합니다.
David Schwartz

@DavidSchwartz 당신은 자바와 다른 플랫폼을 사용해야한다는 것을 의미합니까?
ayvango

@ayvango 여전히 Java 샌드 박스를 사용하여 애플리케이션에서 머신을 보호 할 수 있습니다. 그러나 응용 프로그램의 다른 부분으로부터 응용 프로그램의 일부를 보호하려면이를 수행 할 수있는 도구를 선택해야합니다.
David Schwartz

@DavidSchwartz ASFAIK 이러한 도구는 플랫폼 수준에서 지원되지 않는 경우 존재할 수 없습니다. 물론이 작업은 완전히 해석 된 스크립트 엔진으로 해결할 수 있습니다. erlang처럼 감소를 계산하고 다른 작업을 수행 할 수 있습니다.
ayvango

20

한 가지 방법은 클래스 변수를 설정하고이를 센티넬로 사용하는 것입니다.

Class Outer {
    public static volatile flag = true;

    Outer() {
        new Test().start();
    }
    class Test extends Thread {

        public void run() {
            while (Outer.flag) {
                //do stuff here
            }
        }
    }

}

위의 예제에서 외부 클래스 변수, 즉 flag = true를 설정하십시오. 스레드를 'kill'하려면 false로 설정하십시오.


2
부수적으로 힌트 : 플래그와 같은 변수는 스레드가 실행되고 붙어 있지 않을 때만 작동합니다. Thread.interrupt ()는 대부분의 대기 상태 (대기, 휴면, 네트워크 읽기 등)에서 스레드를 해제해야합니다. 따라서이 작업을 수행하기 위해 InterruptedException을 절대로 포착해서는 안됩니다.
ReneS

12
이것은 신뢰할 수 없습니다. volatile어디에서나 제대로 작동 하도록 "플래그" 를 만듭니다 . 내부 클래스는 정적이 아니므로 플래그는 인스턴스 변수 여야합니다. 액세서 메소드에서 플래그를 지워서 다른 조작 (예 : 인터럽트)을 수행 할 수 있습니다. "플래그"라는 이름은 설명이 아닙니다.
erickson

2
나는 run 메소드에서 "while"을 얻지 못한다. 이것은 run 메소드로 작성된 것이 반복된다는 것을 의미하지 않습니까? 이것은 스레드가 처음에하기를 원하는 것이 아닙니다 :(

2
+1 while (! Thread.currentThread (). isInteruppted ()) 선호
Toby

2
예를 들어 while {// open ext process} 내부에서 외부 프로세스를 열고 해당 프로세스가 중단되면 두 경우 모두 실패합니다. 이제 스레드가 중단되거나 부울 상태를 확인하기 위해 끝에 도달하지 않습니다. 왼쪽 교수형 ... java.exec를 사용하여 Python 콘솔을 시작하고 종료를 쓰지 않고 컨트롤을 다시 가져 와서 프로세스를 종료하고 빠져 나갈 수있는 방법이 있는지 확인하십시오. 그런 상황에서 벗어나 ...
스페이스 로커

11

당신이 그것을 할 수있는 방법이 있습니다. 그러나 당신이 그것을 사용해야한다면, 당신은 나쁜 프로그래머이거나 나쁜 프로그래머가 작성한 코드를 사용하고 있습니다. 따라서 나쁜 프로그래머가되는 것을 멈추거나이 나쁜 코드의 사용을 멈추는 것에 대해 생각해야합니다. 이 솔루션은 다른 방법이없는 상황에만 해당됩니다.

Thread f = <A thread to be stopped>
Method m = Thread.class.getDeclaredMethod( "stop0" , new Class[]{Object.class} );
m.setAccessible( true );
m.invoke( f , new ThreadDeath() );

2
Thread.stop더 이상 사용되지 않더라도 대중에게 전화를 걸 수 있기 때문에이 작업을 수행 할 이유가 전혀 없습니다 .
Lii

1
@Lii Thread.stop는 동일하지만 액세스 및 권한을 확인합니다. 사용 Thread.stop은 다소 명백하며 Thread.stop0그 대신 왜 내가 사용했는지 이유를 기억하지 못합니다 . 어쩌면 Thread.stop나의 특별한 경우 (웹 로직에서 Java 6)을 위해 작동하지 않았다. 또는 Thread.stop더 이상 사용되지 않아 경고가 발생할 수 있습니다.
VadimPlatonov

1
내 시나리오에서 이것은 끝없는 실행중인 스레드를 중지하는 유일한 방법이었습니다. 어떤 이유로 .stop ()은 스레드를 멈추지 않았지만 stop0 ()은
feffy

10

누적 된 주석을 기반으로 여러 관측 값을 추가하고 싶습니다.

  1. Thread.stop() 보안 관리자가 허용하면 스레드를 중지합니다.
  2. Thread.stop()위험합니다. JEE 환경에서 작업 중이고 호출되는 코드를 제어 할 수없는 경우에는 필요할 수 있습니다. 보다Thread.stop이 더 이상 사용되지 않는 이유를 .
  3. 컨테이너 작업자 스레드 중지를 중지해서는 안됩니다. 중단되는 코드를 실행하려면 새 디먼 스레드를 조심스럽게 시작하고 모니터링하여 필요한 경우 종료하십시오.
  4. stop()호출 스레드 에서 새 ThreadDeathError오류를 생성 한 다음 대상 에서 해당 오류를 발생시킵니다. 스레드 . 따라서 스택 추적은 일반적으로 가치가 없습니다.
  5. JRE 6 stop()에서 보안 관리자를 stop1()확인한 다음 해당 호출 을 호출합니다 stop0(). stop0()네이티브 코드입니다.
  6. Java 13 Thread.stop()현재는 아직 제거되지 않았지만Thread.stop(Throwable) Java 11에서는 제거되었습니다. ( mailing list , JDK-8204243 )

8

나는 투표했다 Thread.stop() .

예를 들어 네트워크 요청과 같이 오래 지속되는 작업이 있습니다. 아마도 응답을 기다리는 중이지만 시간이 걸리고 사용자가 다른 UI로 이동했을 수 있습니다. 이 대기 스레드는 이제 a) 쓸모가 없습니다. b) 잠재적 인 문제입니다. 결과를 얻을 때 완전히 쓸모없고 오류가 발생할 수있는 콜백을 트리거하기 때문입니다.

이 모든 것 그리고 그는 CPU를 많이 사용하는 응답 처리를 할 수 있습니다. 그리고 당신은 개발자로서 던질 수 없기 때문에 그것을 막을 수도 없습니다.if (Thread.currentThread().isInterrupted()) 는 모든 코드에 줄을 .

따라서 스레드를 강제로 중지 할 수 없다는 것은 이상합니다.


네트워크 작업이 이미 안전하게 중단되면 스레드를 중단하십시오. 네트워크 작업이 안전하게 중단되지 않으면 Thread.stop()어쨌든 안전하게 전화를 걸 수 없습니다 . 에 투표하지 않고 Thread.stop(), 안전하게 중단하기 위해 오랜 시간이 걸릴 수있는 모든 작업을 수행하는 모든 사람을 요구하고 있습니다. 그리고 그것은 좋은 생각 일 수도 있지만 Thread.stop()안전한 낙태를 요청하는 방법으로 구현하는 것과는 아무런 관련이 없습니다 . 우리는 이미 interrupt그렇게했습니다.
David Schwartz

"따라서 스레드를 강제로 중지 할 수없는 것은 이상합니다." -... (자바 디자이너가했던 것처럼) 깊이 볼 때까지 더 이상 사용되지 않는 기술적으로 가능한 솔루션이 없다고 결론 내릴 수 있습니다 stop.
Stephen C

5

문제는 다소 모호하다. "필요할 때 스레드가 실행을 중지하도록 프로그램을 작성하는 방법"을 의미 한 경우 다양한 기타 응답이 도움이됩니다. 그러나 "서버에 응급 상황이 발생했다면 지금 다시 시작할 수없고 죽을 특정 스레드 만 있으면됩니다."라고 말한 경우 다음과 같은 모니터링 도구와 일치하는 중재 도구가 필요합니다.jstack .

이를 위해 jkillthread를 만들었 습니다 . 사용법은 해당 지침을 참조하십시오.


감사합니다! 정확히 내가 찾던 것!
Denis Kokorin

4

물론 완전히 신뢰할 수없는 코드를 실행하는 경우가 있습니다. (개인적으로 업로드 한 스크립트를 Java 환경에서 실행할 수있게함으로써이 문제를 해결할 수 있습니다. 예, 보안 경보 벨이 울리지 만 응용 프로그램의 일부입니다.)이 불행한 경우에 우선 스크립트 작성자에게 물어 보면 희망이 있습니다. 부울 런 / 런런 금지 신호를 존중합니다. 예를 들어, 시간 초과보다 오래 실행되는 경우 스레드에서 stop 메소드를 호출하는 것이 괜찮은 유일한 안전 장치입니다.

그러나 코드가 ThreadDeath 오류 (또는 명시 적으로 throw하는 예외)를 포착 할 수 있고 신사 스레드처럼 다시 throw하지 않기 때문에 이것은 "괜찮은"것이며 절대적이지는 않습니다. 결론은 AFAIA이며 절대 안전 장치는 없습니다.


AFAIK 점점 더 많은 서비스가 하이브리드 화되고 관리 환경을 사용하여 코드를 완전히 제어 할 수없는 타사 코드 (플러그인, 스크립트 등)를 실행하므로 스레드를 완전히 제거하는 것은 합리적이지 않습니다. 서비스 엔지니어의 경우 라이브 및 서빙 상태가 중단 상태 (스레드를
제거함

3

스레드를 정상적으로 죽일 방법은 없습니다.

스레드를 중단하려고 할 수 있습니다. 일반적인 전략 중 하나는 독 약을 사용하여 스레드 자체를 중지하라는 메시지를 보내는 것입니다.

public class CancelSupport {
    public static class CommandExecutor implements Runnable {
            private BlockingQueue<String> queue;
            public static final String POISON_PILL  = stopnow”;
            public CommandExecutor(BlockingQueue<String> queue) {
                    this.queue=queue;
            }
            @Override
            public void run() {
                    boolean stop=false;
                    while(!stop) {
                            try {
                                    String command=queue.take();
                                    if(POISON_PILL.equals(command)) {
                                            stop=true;
                                    } else {
                                            // do command
                                            System.out.println(command);
                                    }
                            } catch (InterruptedException e) {
                                    stop=true;
                            }
                    }
                    System.out.println(“Stopping execution”);
            }

    }

}

BlockingQueue<String> queue=new LinkedBlockingQueue<String>();
Thread t=new Thread(new CommandExecutor(queue));
queue.put(“hello”);
queue.put(“world”);
t.start();
Thread.sleep(1000);
queue.put(“stopnow”);

http://anandsekar.github.io/cancel-support-for-threads/


2

일반적으로 스레드를 죽이거나 중지하거나 중단하지 않으며 (또는 중단되었는지 확인) 자연스럽게 종료하십시오.

간단하다. run () 메소드 내에서 (휘발성) 부울 변수와 함께 루프를 사용하여 스레드의 활동을 제어 할 수 있습니다. 활성 스레드에서 기본 스레드로 돌아가서 중지 할 수도 있습니다.

이렇게하면 스레드를 정상적으로 죽일 수 있습니다 :).


1

갑작스러운 스레드 종료 시도는 잘 알려진 나쁜 프로그래밍 방식과 응용 프로그램 디자인이 불량하다는 증거입니다. 다중 스레드 응용 프로그램의 모든 스레드는 명시 적 및 암시 적으로 동일한 프로세스 상태를 공유하고 일관성을 유지하기 위해 서로 협력해야합니다. 그렇지 않으면 응용 프로그램이 실제로 진단하기 어려운 버그에 취약합니다. 따라서 신중하고 명확한 응용 프로그램 설계를 통해 이러한 일관성을 보장하는 것은 개발자의 책임입니다.

제어 된 스레드 종료에 대한 두 가지 주요 올바른 솔루션이 있습니다.

  • 공유 휘발성 플래그 사용
  • Thread.interrupt () 및 Thread.interrupted () 메소드 쌍 사용

갑작스러운 스레드 종료와 관련된 문제에 대한 자세하고 자세한 설명과 제어 스레드 종료에 대한 잘못되고 올바른 솔루션의 예는 여기에서 찾을 수 있습니다.

https://www.securecoding.cert.org/confluence/display/java/THI05-J.+Do+not+use+Thread.stop%28%29+to+terminate+threads


더 이상 단일 개발자가 프로그램을 작성하지 않기 때문에 스레드를 죽이는 것이 종종 필요합니다. 따라서 나쁜 프로그래밍으로 간주 될 수 없습니다. 나는 죽이지 않고 리눅스를 상상할 수 없다. 스레드를 죽일 수 없다는 것은 Java 결함입니다.
Pavel Niedoba

1
굉장합니다. 맞습니다 ... 통제 할 수없고 버그가있는 타임 아웃이 있고 실행시 1000-2000 회마다 한 번씩 중단 될 수있는 타사 라이브러리를 호출해야한다면 어떻게해야합니까? 나쁜 프로그래밍 연습? 글쎄, 항상 사용중인 코드에 액세스 할 수는 없습니다. 어쨌든, op는 자신의 코드를 디자인 할 때 흐름을 제어하는 ​​방법이 아닌 스레드를 죽이는 방법을 묻고 있습니다.
Arturas M

문제는 뮤텍스를 소유하거나 일부 메모리 블록이 할당 된 스레드를 죽이거나 다른 스레드가 기다리는 이벤트 또는 데이터를 생성해야하는 경우 어떻게됩니까? 나머지 응용 프로그램 논리는 어떻게됩니까? 작고 명백한 문제가 복잡한 문제로 변환되어 재현 및 조사하기 어려운 경우가 항상 위험합니다. 스레드를 강제 종료하는 것은 응용 프로그램이 여러 가지 다른 일관성없는 상태를 유지할 수 있기 때문에 안전하지 않습니다. cert.org의 링크에서 정보를 살펴보십시오.
ZarathustrA

우리는 특정 조건에서 전체 계산이 무의미하다고 결정하는 제어 스레드를 가지고 있습니다. 실제 작업을 수행하는 여러 가지 실행 가능한 파일은 편리한 위치에 isInterrupted () 변형이 뿌려 져야합니다. 컨트롤러가 ThreadDeath가 아무데도 보이지 않게 상승하여 모든 동기화 된 블록과 마지막 블록을 깨끗하게 풀면 훨씬 좋습니다. 모두들 이것이 오류가 발생하기 쉽다고 말하지만 상당히 관련이없는 스레드의 경우 이유를 알 수 없습니다.
다니엘


1

Android에서 인터럽트가 작동하지 않았 으므로이 방법을 사용하여 완벽하게 작동합니다.

boolean shouldCheckUpdates = true;

private void startupCheckForUpdatesEveryFewSeconds() {
    Thread t = new Thread(new CheckUpdates());
    t.start();
}

private class CheckUpdates implements Runnable{
    public void run() {
        while (shouldCheckUpdates){
            //Thread sleep 3 seconds
            System.out.println("Do your thing here");
        }
    }
}

 public void stop(){
        shouldCheckUpdates = false;
 }

2
컴파일러가 스레드 로컬 스토리지로 최적화하는 경우 volatile 키워드를 shouldCheckUpdates에 추가해야합니다.
clinux 5

1

'실을 죽이는 것'은 올바른 문구가 아닙니다. 다음은 마음대로 스레드의 완성 / 종료를 구현할 수있는 한 가지 방법입니다.

내가 사용한 Runnable :

class TaskThread implements Runnable {

    boolean shouldStop;

    public TaskThread(boolean shouldStop) {
        this.shouldStop = shouldStop;
    }

    @Override
    public void run() {

        System.out.println("Thread has started");

        while (!shouldStop) {
            // do something
        }

        System.out.println("Thread has ended");

    }

    public void stop() {
        shouldStop = true;
    }

}

트리거링 클래스 :

public class ThreadStop {

    public static void main(String[] args) {

        System.out.println("Start");

        // Start the thread
        TaskThread task = new TaskThread(false);
        Thread t = new Thread(task);
        t.start();

        // Stop the thread
        task.stop();

        System.out.println("End");

    }

}

컴파일러가 스레드 로컬 스토리지로 최적화하는 경우 volatile 키워드를 shouldStop 변수에 추가해야합니다.
Oleksii Kyslytsyn

0

Thread.stop은 더 이상 사용되지 않으므로 java에서 스레드를 어떻게 중지합니까?

항상 인터럽트 방법과 미래를 사용하여 취소를 요청하십시오.

  1. 작업이 인터럽트 신호 (예 : 블로킹 큐 가져 오기 메소드)에 응답하는 경우
Callable < String > callable = new Callable < String > () {
    @Override
    public String call() throws Exception {
        String result = "";
        try {
            //assume below take method is blocked as no work is produced.
            result = queue.take();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return result;
    }
};
Future future = executor.submit(callable);
try {
    String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    logger.error("Thread timedout!");
    return "";
} finally {
    //this will call interrupt on queue which will abort the operation.
    //if it completes before time out, it has no side effects
    future.cancel(true);
}
  1. 태스크가 인터럽트 신호에 응답하지 않는 경우 태스크가 인터럽트 신호에 응답하지 않는 소켓 I / O를 수행하므로 위의 접근 방식을 사용하면 태스크가 중단되지 않습니다. 스레드는 소켓을 계속 청취합니다. 풀로 구현 된 경우 연결시 소켓을 닫거나 닫기 메소드를 호출 할 수 있습니다.
public interface CustomCallable < T > extends Callable < T > {
    void cancel();
    RunnableFuture < T > newTask();
}

public class CustomExecutorPool extends ThreadPoolExecutor {
    protected < T > RunnableFuture < T > newTaskFor(Callable < T > callable) {
        if (callable instanceof CancellableTask)
            return ((CancellableTask < T > ) callable).newTask();
        else
            return super.newTaskFor(callable);
    }
}

public abstract class UnblockingIOTask < T > implements CustomCallable < T > {
    public synchronized void cancel() {
        try {
            obj.close();
        } catch (IOException e) {
            logger.error("io exception", e);
        }
    }

    public RunnableFuture < T > newTask() {
        return new FutureTask < T > (this) {
            public boolean cancel(boolean mayInterruptIfRunning) {
                try {
                    this.cancel();
                } finally {
                    return super.cancel(mayInterruptIfRunning);
                }
            }

        };
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.