Java에서 "실행 가능 구현"과 "스레드 확장"


2118

Java에서 스레드를 사용한 시간부터 스레드를 작성하는 다음 두 가지 방법을 찾았습니다.

implements Runnable:

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

또는 extends Thread:

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

이 두 코드 블록에 큰 차이가 있습니까?


57
이 질문에 대한 감사의 답변으로 내가 가진 많은 오해가 해결되었습니다. SO가 존재하기 전에 Java 스레드를 수행하는 올바른 방법을 조사했으며 잘못된 정보 / 오래된 정보가 많이있었습니다.
James McMahon

5
Thread를 확장하고 싶은 한가지 이유가 있지만 ( 권장하지는 않음 ), 선제 적으로 처리 할 수 ​​있습니다 interrupt(). 다시 말하지만, 올바른 경우에는 유용 ​​할 수 있지만 권장하지는 않습니다.
bestsss

또한 대답을 참조하십시오 멋지게 설명 : stackoverflow.com/q/5562720/285594

@ bestsss, 나는 당신이 인터럽트 () 처리에 대해 무엇을 의미하는지 퍼즐하려고합니다. 메소드를 대체하려고합니까?
밥 크로스

8
코드 당 yes.As는 Thread 클래스 A는 다른 클래스 확장하지 못할 수준의 스레드 B 반면 어떤 클래스를 확장 할 수 있습니다
마니 디팍

답변:


1672

예 : 구현 Runnable이 선호되는 방법 인 IMO입니다. 스레드의 동작을 실제로 전문화하지는 않습니다. 당신은 그것을 실행할 무언가를주고 있습니다. 그것은 구성철학적으로 "순결한"방법 임을 의미 합니다.

에서 실제적인 용어, 당신이 구현할 수 있음을 의미 Runnable하고 다른 클래스에서 확장뿐만 아니라.


151
정확히 말해서 Thread를 확장하여 어떤 동작을 덮어 쓰려고합니까? 나는 대부분의 사람들이 행동을 덮어 쓰려고 시도하지 않고 Thread의 행동을 사용하려고한다고 주장합니다.
hooknc

87
부수적으로 Thread를 인스턴스화하고 start () 메소드를 호출하지 않으면 Java <5에서 메모리 누수가 발생합니다 (Runnables에서는 발생하지 않음) : stackoverflow.com/questions/107823/…
Nacho Coloma

25
Runnable의 작은 장점 중 하나는 특정 상황에서 스레딩을 신경 쓰지 않거나 쓰지 않으려는 경우 코드를 실행하려는 경우 run ()을 호출하는 옵션이 있다는 것입니다. 예 : (매우 손으로) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
user949300

10
@ user949300 당신은 또한 그것을 할 수 extends Thread있고 만약 당신이 스레딩을 원하지 않는다면 왜 당신도 구현 Runnable
하겠습니까

30
Sierra와 Bates를 역설적으로 표현하면 Runnable을 구현할 때 얻을 수있는 주요 이점은 "작업자"와 "작업자"를 구조적으로 분리한다는 것입니다.
8bitjunkie

569

tl; dr : Runnable이 더 좋습니다. 그러나주의 사항은 중요합니다

일반적으로 작업을 선택한 동시성 작업과 느슨하게 결합 할 수 있기 때문에 Runnable오히려 비슷한 것을 사용하는 것이 좋습니다 Thread. 예를 들어, a를 사용하고 Runnable나중에 이것이 실제로 필요하지 않다고 결정 Thread하면 threadA.run ()을 호출하면됩니다.

주의 사항 : 여기에서는 원시 스레드를 사용하지 않는 것이 좋습니다. 나는 CallablesFutureTasks를 사용하는 것을 선호한다 (javadoc에서 : "취소 가능한 비동기 계산"). 타임 아웃의 통합, 적절한 취소 및 현대 동시성 지원의 스레드 풀링은 원시 스레드 더미보다 훨씬 유용합니다.

후속 조치 : Runnables (가장 익숙한 경우)를 사용하고 최신 동시성 도구의 이점을 얻을 수 있는 FutureTask생성자 가 있습니다. javadoc을 인용하려면 :

특정 결과가 필요하지 않은 경우 다음 형식의 구성을 사용하십시오.

Future<?> f = new FutureTask<Object>(runnable, null)

따라서 이들을 runnable귀하의 threadA로 바꾸면 다음과 같은 결과를 얻습니다.

new FutureTask<Object>(threadA, null)

Runnables에 더 가까이 머물 수있는 또 다른 옵션은 ThreadPoolExecutor 입니다. execute 메소드를 사용하여 Runnable을 전달하여 "언제나 주어진 작업을 실행"할 수 있습니다.

스레드 풀을 사용하려면 위의 코드 조각이 다음과 같이됩니다 ( Executors.newCachedThreadPool () 팩토리 메소드 사용).

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());

40
이것은 정답 IMHO보다 낫습니다. 한 가지 : 당신이 가지고있는 코드 스 니펫은 집행자를 닫지 않으며 사람들 이이 문제를 일으키는 수백만 가지 질문을보고 작업을 생성 할 때마다 새로운 집행자를 만듭니다. es정적 (또는 주입 된) 필드로 더 좋으므로 한 번만 생성됩니다.
artbristol

7
@ artbristol, 감사합니다! 나는 새로운 집행 인에 동의하지 않습니다 (우리는 당신이 우리의 코드에서 제안한 것을합니다). 원래의 답변을 쓸 때, 나는 원래 코드에 최소한의 코드를 작성하려고했습니다. 우리는이 답변을 읽는 많은 독자들이 그것들을 점으로 뛰어 넘기를 희망합니다. javadoc을 대체하려고하지 않습니다. 마케팅 자료를 효과적으로 작성하고 있습니다. 이 방법이 마음에 들면 우리가 제공해야 할 다른 모든 위대한 것들을 보게 될 것입니다 ...!
밥 크로스

3
나는 이것에 대해 조금 늦게 알고 있지만 FutureTask직접 처리하는 것은 일반적으로 당신이하고 싶은 것이 아닙니다. 당신 이 그들에게 / 때 당신을 위해 ExecutorService적절한 만들 것입니다. 마찬가지로위한 s와 때를 / . FuturesubmitRunnableCallableScheduledExecutorServiceScheduledFuturescheduleRunnableCallable
Powerlord

3
@Powerlord, 내 의도는 가능한 한 OP와 일치하는 코드 조각을 만드는 것이 었습니다. 새로운 FutureTask가 최적이 아니지만 설명의 목적으로 명확하다는 데 동의합니다.
Bob Cross

257

이야기의 교훈:

일부 동작을 재정의하려는 경우에만 상속하십시오.

또는 오히려 다음과 같이 읽어야합니다.

상속이 적고 인터페이스가 더 많습니다.


1
동시에 실행중인 Object를 만들기 시작하면 항상 문제가됩니다! 스레드 객체 기능도 필요합니까?
Liebertee

4
Thread에서 상속 할 때 거의 항상 run()메소드 의 동작을 재정의하려고합니다 .
Warren Dew

1
메소드 java.lang.Thread를 대체하여 a의 동작을 대체 할 수 없습니다 run(). 이 경우 start()내가 생각 하는 방법 을 재정의해야합니다 . 일반적으로 메소드 java.lang.Thread에 실행 블록을 삽입하여 의 동작을 재사용합니다 run().
sura2k

상속은 일부 동작을 재정의하기위한 것이 아니라 일반적인 동작을 사용하는 것입니다. 그리고 반대로, 재정의가 많을수록 계층 구조가 악화됩니다.
peja

232

좋은 답변이 너무 많아서 더 추가하고 싶습니다. 이것은 이해하는 데 도움이됩니다 Extending v/s Implementing Thread.
Extends는 두 클래스 파일을 매우 밀접하게 바인딩하며 코드를 다루기가 다소 어려울 수 있습니다.

두 방법 모두 동일한 작업을 수행하지만 약간의 차이가 있습니다.
가장 일반적인 차이점은

  1. Thread 클래스를 확장하면 그 후에 필요한 다른 클래스를 확장 할 수 없습니다. 아시다시피 Java는 둘 이상의 클래스 상속을 허용하지 않습니다.
  2. Runnable을 구현하면 나중에 또는 지금 다른 클래스를 확장 할 수있는 클래스 공간을 절약 할 수 있습니다.

그러나 Runnable 구현과 Thread 확장의 중요한 차이점
by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.

다음 예제는 더 명확하게 이해하는 데 도움이됩니다.

//Implement Runnable Interface...
 class ImplementsRunnable implements Runnable {

private int counter = 0;

public void run() {
    counter++;
    System.out.println("ImplementsRunnable : Counter : " + counter);
 }
}

//Extend Thread class...
class ExtendsThread extends Thread {

private int counter = 0;

public void run() {
    counter++;
    System.out.println("ExtendsThread : Counter : " + counter);
 }
}

//Use the above classes here in main to understand the differences more clearly...
public class ThreadVsRunnable {

public static void main(String args[]) throws Exception {
    // Multiple threads share the same object.
    ImplementsRunnable rc = new ImplementsRunnable();
    Thread t1 = new Thread(rc);
    t1.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread t2 = new Thread(rc);
    t2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread t3 = new Thread(rc);
    t3.start();

    // Creating new instance for every thread access.
    ExtendsThread tc1 = new ExtendsThread();
    tc1.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc2 = new ExtendsThread();
    tc2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc3 = new ExtendsThread();
    tc3.start();
 }
}

위 프로그램의 출력.

ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1

Runnable 인터페이스 방식에서는 하나의 클래스 인스턴스 만 작성되며 다른 스레드에서 공유했습니다. 따라서 카운터 값은 모든 스레드 액세스마다 증가합니다.

반면 스레드 클래스 접근 방식은 모든 스레드 액세스에 대해 별도의 인스턴스를 작성해야합니다. 따라서 모든 클래스 인스턴스에 대해 서로 다른 메모리가 할당되고 각각의 카운터에는 별도의 카운터가 있으며 값은 동일하게 유지되므로 개체 참조가 같지 않으므로 증분이 발생하지 않습니다.

Runnable을 언제 사용해야합니까?
스레드 그룹에서 동일한 자원에 액세스하려는 경우 실행 가능 인터페이스를 사용하십시오. 여러 객체를 만들 때 더 많은 메모리를 사용하고 성능 오버 헤드가 커지므로 여기에서 Thread 클래스를 사용하지 마십시오.

Runnable을 구현하는 클래스는 스레드가 아니라 클래스입니다. Runnable이 스레드가 되려면 Thread 인스턴스를 작성하고 대상으로 전달해야합니다.

대부분의 경우 run()메서드 를 재정의하고 다른 스레드 메서드 는 무시하려는 경우 Runnable 인터페이스를 사용해야합니다 . 프로그래머가 클래스의 기본 동작을 수정하거나 향상시키려는 경우가 아니라면 클래스를 서브 클래스 화해서는 안되므로 이것은 중요합니다.

수퍼 클래스를 확장해야하는 경우 Thread 클래스를 사용하는 것보다 Runnable 인터페이스를 구현하는 것이 더 적합합니다. Runnable 인터페이스를 구현하는 동안 다른 클래스를 확장하여 스레드를 만들 수 있기 때문입니다.

이것이 도움이되기를 바랍니다!


40
코드가 잘못되었습니다. 내 말은, 그것이하는 일을하지만 당신이 보여 주려는 의도는 아닙니다.
zEro

38
명확히하기 위해 : 실행 가능 사례의 경우 동일한 ImplementsRunnable 인스턴스를 사용하여 여러 스레드를 시작한 반면 스레드 사례의 경우 다른 ExtendsThread 인스턴스를 작성하여 분명히 표시된 동작으로 이어집니다. 주요 방법의 후반부는 다음과 같아야합니다. ExtendsThread et = new ExtendsThread(); Thread tc1 = new Thread(et); tc1.start(); Thread.sleep(1000); Thread tc2 = new Thread(et); tc2.start(); Thread.sleep(1000); Thread tc3 = new Thread(et); tc3.start(); 더 명확합니까?
zEro

19
나는 아직 당신의 의도를 이해하지 못하지만, 내 요점은 ExtendsThread의 인스턴스를 여러 개 만들면 모두 1을 반환한다는 것입니다. ImplementsRunnable의 여러 인스턴스를 작성하여 동일한 작업을 수행하여 Runnable에 대해 동일한 결과를 얻을 수 있습니다.
zEro

3
@zEro 안녕하세요, 저는 미래에서 왔습니다. 코드 버전도 Thread증가하고 있다고 가정하면 문이 by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance잘못 되었습니까? 그렇지 않다면, 이것을 보여주는 사례는 무엇입니까?
사악한 세탁기

4
여기에 게시 된 코드는 오해의 소지가 있으며 두통을 유발할 수 있습니다. 조금 정리해 주신 @zEro에게 감사드립니다.
Nikos

78

놀랍게도 아직 언급되지 않은 한 가지는 구현 Runnable이 수업을보다 유연하게 만든다는 것입니다.

스레드를 확장하면 수행중인 작업은 항상 스레드에 있습니다. 그러나 구현하면 반드시 Runnable그럴 필요는 없습니다. 스레드에서 실행하거나 일종의 executor 서비스에 전달하거나 단일 스레드 응용 프로그램 내에서 작업으로 전달할 수 있습니다 (나중에 동일한 스레드 내에서 실행될 수도 있음). Runnable자신을 묶는 것보다 그냥 사용하면 옵션이 훨씬 더 열려 있습니다 Thread.


6
글쎄, 당신은 실제로과 같은 일을 할 수 Thread도 있기 때문에 객체 Thread implements Runnable... ;-) 그러나 "Feel로 더 나은"는 함께이 일을 Runnable로하는 것보다 Thread!
siegi

7
사실이지만 Thread필요하지 않은 많은 추가 항목을 추가하고 많은 경우 원하지 않습니다. 실제로하고있는 것과 일치하는 인터페이스를 구현하는 것이 좋습니다.
Herms

74

다른 클래스를 구현하거나 확장하려면 Runnable인터페이스가 가장 바람직하고, 그렇지 않으면 다른 클래스를 확장하거나 구현하지 않으려면 Thread클래스가 바람직합니다.

가장 일반적인 차이점은

여기에 이미지 설명을 입력하십시오

때를 extends Thread 클래스, 당신은 당신이 필요한 다른 클래스를 확장 할 수 없습니다 그 후. 아시다시피 Java는 둘 이상의 클래스 상속을 허용하지 않습니다.

implements Runnable경우 미래 ​​또는 현재 다른 클래스를 확장 할 수있는 클래스 공간을 절약 할 수 있습니다.

  • Java는 다중 상속을 지원하지 않습니다. 즉, Java에서 하나의 클래스 만 확장 할 수 있으므로 스레드 클래스를 확장하면 기회가 없어지고 Java에서 다른 클래스를 확장하거나 상속 할 수 없습니다.

  • 객체 지향 프로그래밍에서 클래스 확장은 일반적으로 새로운 기능 추가, 동작 수정 또는 개선을 의미합니다. Thread를 수정하지 않으면 Runnable 인터페이스를 대신 사용하십시오.

  • 실행 가능 인터페이스는 일반 스레드 또는 실행자 또는 다른 방법으로 실행할 수있는 작업을 나타냅니다. 따라서 Thread보다 Runnable로 Task를 논리적으로 분리하는 것이 좋은 디자인 결정입니다.

  • 작업을 실행 가능으로 분리한다는 것은 작업을 재사용 할 수 있고 다른 방법으로 작업을 수행 할 자유가 있다는 것을 의미합니다. 스레드가 완료되면 다시 시작할 수 없으므로 Runnable vs Thread for task, Runnable이 승자입니다.

  • Java 디자이너는 이것을 인식하고 Executor가 Runnable을 Task로 받아들이고 그 작업을 실행하는 작업자 스레드가 있습니다.

  • 모든 Thread 메소드를 상속하는 것은 Runnable로 쉽게 수행 할 수있는 Task를 나타 내기위한 추가 오버 헤드입니다.

javarevisited.blogspot.com의 의례

이것들은 Java에서 Thread와 Runnable의 주목할만한 차이점 중 일부였습니다. Thread vs Runnable의 다른 차이점을 알고 있다면 의견을 통해 공유하십시오. 이 시나리오에서는 개인적으로 스레드 위의 Runnable을 사용하며 요구 사항에 따라 Runnable 또는 Callable 인터페이스를 사용하는 것이 좋습니다.

그러나 중요한 차이점입니다.

extends Thread수업을 할 때 각 스레드는 고유 한 객체를 만들어 연결합니다. 때 implements Runnable, 그것은 여러 스레드에 동일한 개체를 공유합니다.


73

사실, 비교하는 것이 현명하지 않다 RunnableThread서로.

이 두 가지는 Wheel and Engine자동차의 관계 와 마찬가지로 멀티 스레딩에서 의존성과 관계를 갖습니다 .

나는 두 단계로 멀티 스레딩을위한 한 가지 방법 만 있다고 말합니다. 내가 지적하도록하겠습니다.

실행 가능 :
구현할 때 다른 스레드에있는 interface Runnable것을 작성하고 있음을 의미합니다 run able. 이제 스레드 내부에서 실행할 수있는 무언가 (스레드 내부에서 실행 가능)를 생성한다고해서 스레드를 생성하는 것은 아닙니다.
따라서 클래스 MyRunnablevoid run메소드가 있는 일반 클래스 일뿐 입니다. 그리고 객체는 run호출 될 때 정상적으로 실행될 메소드 만 가진 일부 일반 객체입니다 . (객체를 스레드로 전달하지 않는 한).

Thread :
class Thread , 나는 실제로 그 start()방법을 통해 멀티 스레딩을 가능하게하는 새로운 Thread를 시작할 수있는 매우 특별한 클래스라고 말할 것 입니다.

왜 현명하게 비교하지 않습니까?
멀티 스레딩에는 둘 다 필요하기 때문입니다.

멀티 스레딩에는 두 가지가 필요합니다.

  • 스레드 내에서 실행할 수있는 것 (실행 가능).
  • 새로운 스레드 (Thread)를 시작할 수있는 것.

따라서 기술적으로 이론적으로 두 가지 모두 스레드를 시작하는 데 필요합니다. 하나는 실행 되고 하나는 ( 자동차 와 마찬가지로) 실행Wheel and Engine 됩니다.

그렇기 때문에 스레드를 시작할 수 없으므로 MyRunnable인스턴스를에 전달해야합니다 Thread.

그러나class Thread 클래스 Thread구현을 사용하여 스레드를 작성하고 실행할 수 Runnable있으므로 우리 모두 내부에 있음을 알 수 Thread있습니다 Runnable.

마지막으로 ThreadRunnable경쟁하지 또는 교체를 멀티 스레딩을 위해 서로 보완된다.


3
바로 그거죠! 이것이 정답입니다. BTW 질문이 편집되었고 ThreadA더 이상 의미가 없다고 생각합니다
idelvall

@idelvall
Saif

가장 좋은 답변! 감사!
MichaelYe

44

Runnable을 구현해야하지만 Java 5 이상에서 실행중인 경우 시작하지 말고 대신 ExecutorServicenew Thread사용해야합니다 . 자세한 내용은 Java에서 단순 스레딩을 구현하는 방법을 참조하십시오 .


5
단일 스레드를 시작하려는 경우 ExecutorService가 그렇게 유용하지 않을 것이라고 생각합니다.
Powerlord 2012

1
내가 배운 것에서 더 이상 일반적으로 스레드를 시작해서는 안됩니다. 실행기 서비스에 남겨두면 스레드가 일시 중단되기를 기다리는 것과 같이 훨씬 더 제어 가능하기 때문입니다. 또한, 질문에 단일 스레드에 관한 암시가 없습니다.
Fabian Steeg

4
멀티 스레딩이 단일 스레드가 될 것이라는 점을 알고 있다면 멀티 스레딩을 사용하는 것이 중요합니다. 따라서 여러 스레드가 있고이 답변이 가치 있다고 가정합시다.
zEro

1
@zEro 이벤트 디스패치 스레드가 하나 밖에없는 이유가 있다고 확신합니다. 별도의 스레드를 갖는 것이 가장 좋지만 여러 개를 갖는 것이 가장 좋지 않은 경우는 의심 스럽습니다.
porcoesphino

33

나는 전문가가 아니지만 확장 스레드 대신 Runnable을 구현 해야하는 한 가지 이유를 생각할 수 있습니다 .Java는 단일 상속 만 지원하므로 하나의 클래스 만 확장 할 수 있습니다.

편집 : 원래는 "인터페이스를 구현하는 데 적은 리소스가 필요합니다."라고 말했습니다. 또한 어느 쪽이든 새 Thread 인스턴스를 만들어야하므로 잘못되었습니다.


실행 가능한 상태에서 네트워크 호출을 할 수 없습니까? android.os.NetworkOnMainThreadException이 발생했습니다. 그러나 스레드를 사용하면 네트워크 호출을 할 수 있습니다. 내가 틀렸다면 정정 해주세요.
Nabeel Thobani

@NabeelThobani 일반 Java는 신경 쓰지 않지만 Android처럼 들립니다. 그래도 안드로이드에 익숙하지 않습니다.
Powerlord

1
@NabeelThobani 물론 가능합니다. 아마도 Runnable을 사용하여 스레드를 작성하지 않았을 것입니다.
m0skit0

20

나는 세 번째 방법이 있다고 말할 것입니다.

public class Something {

    public void justAnotherMethod() { ... }

}

new Thread(new Runnable() {
   public void run() {
    instanceOfSomething.justAnotherMethod();
   }
}).start();

어쩌면 이것은 최근 Javascript 및 Actionscript 3을 많이 사용하여 약간의 영향을받지 만 클래스는와 같은 모호한 인터페이스를 구현할 필요가 없습니다 Runnable.


38
이것은 실제로 세 번째 방법이 아닙니다. 여전히 익명으로 실행하는 Runnable을 구현하고 있습니다.
Don Roby

2
@ 돈 롭 : 다른 것입니다. 종종 편리하며 포함하는 클래스 / 방법에서 필드 및 최종 지역 변수를 사용할 수 있습니다.
바트 반 Heukelom

6
@BartvanHeukelom 편리하지만 다르지 않습니다. 내부 클래스, 로컬 클래스 및 람다 식과 같은 모든 유형의 중첩 클래스를 사용하여이 작업을 수행 할 수 있습니다.
xehpuk

18

Java 8 릴리스에는 세 번째 옵션이 있습니다.

RunnableA는 기능적 인터페이스 그것의 인스턴스 람다 식 또는 방법 참조하여 생성 할 수 있음을 의미는.

귀하의 예는 다음과 같이 대체 될 수 있습니다.

new Thread(() -> { /* Code here */ }).start()

또는 ExecutorServiceand 메소드 참조 를 사용하려는 경우 :

executor.execute(runner::run)

이것들은 당신의 예제보다 훨씬 짧을뿐만 아니라 스레드의 동작을 전문화하지 않기 때문에 단일 책임과 구성 사용과 같은 Runnableover 사용 에 대한 다른 답변에 언급 된 많은 이점을 제공합니다 Thread. 이 방법을 사용하면 Runnable예제에서와 같이 필요한 모든 클래스를 추가로 만들지 않아도 됩니다.


이 답변에는 설명이 필요합니다. 약간의 수수께끼 후, 나는 () -> {}그것이 누군가가 필요로하는 사용자 정의 논리를 나타내는 것으로 결론 지었 습니까? 그래서 그것은 더 잘 말할 것 () -> { /* Code here */ }입니까?
ToolmakerSteve

17

인터페이스를 인스턴스화하면 코드와 스레드 구현을 명확하게 분리 할 수 ​​있으므로이 경우 Runnable을 구현하는 것이 좋습니다.


12

여기에있는 모든 사람들은 Runnable을 구현하는 것이 갈 길이라고 생각하고 실제로 의견에 동의하지 않지만 내 의견으로는 Thread를 확장하는 경우도 있습니다. 실제로 코드에서 설명했습니다.

Runnable을 구현하면 Runnable을 구현하는 클래스는 스레드 이름을 제어 할 수 없으며 스레드 이름을 설정할 수있는 호출 코드입니다.

new Thread(myRunnable,"WhateverNameiFeelLike");

그러나 Thread를 확장하면 클래스 자체에서이를 관리하게됩니다 (예와 같이 스레드 이름을 'ThreadB'로 지정). 이 경우에는

A) 디버깅 목적으로 더 유용한 이름을 줄 수 있습니다.

B) 그 이름을 해당 클래스의 모든 인스턴스에 사용하도록 강요하고 있습니다 (스레드라는 사실을 무시하고 Runnable 인 것처럼 위와 같이하지는 않지만 여기서는 관습에 대해 이야기하고 있습니다) 내가 느끼는 그 가능성을 무시하십시오).

예를 들어 생성의 스택 추적을 가져와 스레드 이름으로 사용할 수도 있습니다. 이것은 이상하게 보일 수 있지만 코드 구성 방식에 따라 디버깅 목적으로 매우 유용 할 수 있습니다.

이것은 작은 것 같지만 많은 스레드가있는 매우 복잡한 응용 프로그램이 있고 갑작스런 일이 '중지되었습니다'(교착 상태 또는 네트워크 프로토콜의 결함으로 인해) 명백한-또는 다른 끝없는 이유) 그런 다음 모든 스레드가 'Thread-1', 'Thread-2', 'Thread-3'이라고하는 Java에서 스택 덤프를 얻는 것이 항상 유용한 것은 아닙니다 (스레드가 어떻게 작동하는지에 달려 있습니다) 스택 추적으로 어떤 것을 추적 할 수 있는지 유용하게 알 수 있는지 여부-동일한 코드를 실행하는 여러 스레드 그룹을 사용하는 경우 항상 가능하지는 않습니다).

물론 이름을 생성 호출의 스택 추적으로 설정하고 표준 Java Thread 클래스 대신 Runnable 구현과 함께 사용하는 스레드 클래스의 확장을 작성하여 일반적인 방법으로 위의 작업을 수행 할 수 있다고 말 했음 (아래 참조) 스택 추적 외에도 디버깅을 위해 스레드 이름에 유용한 더 많은 컨텍스트 특정 정보가있을 수 있습니다 (예 : 처리 할 수있는 많은 대기열 또는 소켓 중 하나에 대한 참조) 이 경우 스레드를 구체적으로 확장하여 컴파일러가 (또는 라이브러리를 사용하는 다른 사람들이) 이름에 사용하기 위해 특정 정보 (예 : 해당 큐 / 소켓)를 전달하도록 할 수 있습니다.

다음은 호출 스택 추적을 이름으로 사용하는 일반 스레드의 예입니다.

public class DebuggableThread extends Thread {
    private static String getStackTrace(String name) {
        Throwable t= new Throwable("DebuggableThread-"+name);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        t.printStackTrace(ps);
        return os.toString();
    }

    public DebuggableThread(String name) {
        super(getStackTrace(name));
    }

    public static void main(String[] args) throws Exception {
        System.out.println(new Thread());
        System.out.println(new DebuggableThread("MainTest"));
    }
}

다음은 두 이름을 비교 한 출력 샘플입니다.

Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
    at DebuggableThread.getStackTrace(DebuggableThread.java:6)
    at DebuggableThread.<init>(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]

당신의 요점은 무엇입니까? 스레드를 실행하는 동안 위의 코드를 사용하여 스레드 작성의 스택 추적을 얻을 수는 없지만 (단순한 이름을 얻거나 스레드 시작의 스택 추적을 얻는 대신) 스레드를 서브 클래스 화하면 정확히 수행하고 강제 할 수 있습니다 상황에 맞는 추가 정보가 필요하기 때문에 어떤 스레드에 문제가 있는지 정확하게 이해할 수 있습니다.
AntonyM

8
내 요점은 "Runnable을 구현하면 Runnable을 구현하는 클래스는 스레드 이름을 제어 할 수 없습니다 ..."라는 것은 잘못된 것입니다. Runnable코드를 실행하는 스레드는 현재 스레드 이므로 보안 검사를 통과하는 모든 코드 는 스레드 이름을 제어하므로 클래스 구현 은 실제로 스레드 이름을 제어 할 수 있습니다 . 게시물의 절반을 "omg, 스레드 이름은 어떻습니까!"에 전념하는 것을 고려하면 다소 큰 일처럼 보입니다.
cHao

실 이름? 스레드 클래스 확장을 멈추는 것은 없습니다.
RichieHH

11

다음과 같은 이유로 실행할 수 있습니다.

  • Runnable 구현이 다른 클래스를 확장 할 수 있도록 더 많은 유연성을 제공합니다.
  • 코드를 실행에서 분리
  • 스레드 풀, 이벤트 스레드 또는 향후 다른 방법으로 실행 가능 파일을 실행할 수 있습니다.

지금은이 중 어느 것도 필요하지 않더라도 앞으로있을 수 있습니다. Thread를 재정의하는 데는 이점이 없으므로 Runnable이 더 나은 솔루션입니다.


11

이 주제는 매우 인기있는 주제이고 좋은 답변이 널리 퍼져 있고 심층적으로 다루어 졌기 때문에 다른 사람들의 좋은 답변을보다 간결한 형식으로 컴파일하는 것이 타당하다고 생각했기 때문에 신규 이민자들은 쉽게 개관 할 수 있습니다.

  1. 일반적으로 클래스를 확장하여 기능을 추가하거나 수정합니다. 그래서, 당신이 원하지 않는 경우덮어 어떤 스레드 동작을 , 다음의 Runnable을 사용합니다.

  2. 같은 관점에서, 당신이 필요하지 않은 경우상속 스레드 방법, 당신은없이 할 수있는 오버 헤드 의 Runnable를 사용하여.

  3. 단일 상속 : Thread를 확장하면 다른 클래스에서 확장 할 수 없으므로 필요한 경우 Runnable을 사용해야합니다.

  4. 그것은 그런 의미에서가의 Runnable 태스크 가지고하는 것이 좋습니다, 기술적 수단에서 별도의 도메인 로직에 좋은 디자인입니다 분리 하여 에서 작업을 하여 주자 .

  5. 동일한 Runnable 객체를 여러 번 실행할 수 있지만 Thread 객체는 한 번만 시작할 수 있습니다. (이유로 Executor가 Runnable을 허용하지만 Thread는 허용하지 않는 이유 일 수 있습니다.)

  6. Runnable로 작업을 개발 하면 현재와 미래에 작업 을 사용할 수있는 유연성이 있습니다 . 실행기를 통해 동시에 스레드를 통해 실행할 수 있습니다. 그리고 다른 일반 유형 / 객체와 마찬가지로 동일한 스레드 내에서 비동 시적으로 사용 / 호출 할 수도 있습니다.

  7. 이것은에 그것은 또한 쉽게 별도의 태스크 논리와 동시성 의 측면 당신의 단위 테스트 .

  8. 이 질문에 관심이 있다면 Callable과 Runnable차이점에 관심이있을 수도 있습니다 .


@Pino 예. Thread 자체도 Runnable입니다. 그러나 Runnable로 사용하도록 확장하면 요점이 무엇입니까? 모든 수하물없이 일반 Runnable을 사용하는 것이 어떻습니까? 따라서 Thread를 확장하면 start 메소드를 사용하여 스레드를 실행할 수도 있다고 주장합니다.이 메소드는 한 번만 사용할 수 있습니다. 이것이 니 디쉬-크리 쉬난이 그의 대답에서 원했던 요점입니다. 내 것은 여기에 다른 답변을 요약 한 것입니다.
Jörg

11

확장 스레드와 Runnable 구현의 차이점은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오


8

이는 Oracle의 스레드 정의 및 시작 학습서에서 설명합니다.

이 관용구 중 어느 것을 사용해야합니까? Runnable 객체를 사용하는 첫 번째 관용구가 더 일반적입니다. Runnable 객체는 Thread 이외의 클래스를 서브 클래스화할 수 있기 때문입니다. 두 번째 관용구는 간단한 응용 프로그램에서 사용하기 쉽지만 작업 클래스가 Thread의 자손이어야한다는 사실에 의해 제한됩니다. 이 레슨에서는 실행 가능한 태스크를 태스크를 실행하는 Thread 오브젝트와 분리하는 첫 번째 접근법에 중점을 둡니다. 이 접근 방식은보다 유연 할뿐만 아니라 나중에 다루는 고급 스레드 관리 API에도 적용 할 수 있습니다.

즉, Runnable클래스가 이외의 클래스를 확장하는 시나리오에서 구현 이 작동합니다 Thread. Java는 다중 상속을 지원하지 않습니다. 또한 Thread일부 고급 스레드 관리 API를 사용할 때는 확장 할 수 없습니다. 확장 Thread이 선호 되는 유일한 시나리오 는 향후 업데이트가 적용되지 않는 소규모 응용 프로그램입니다. Runnable프로젝트가 커질 수록 유연성이 높아 지므로 구현하는 것이 거의 항상 좋습니다 . 자바로 많은 인터페이스를 구현할 수 있기 때문에 디자인 변경은 큰 영향을 미치지 않지만 하나의 클래스 만 확장합니다.


8

가장 간단한 설명은 Runnable동일한 객체를 여러 스레드에 할당 할 수 있다는 것입니다.Thread 동일한 객체 상태와 동작을 공유한다는 것입니다.

예를 들어 스레드가 두 개 있다고 가정합니다. thread1 은 배열에 정수를 넣고 thread2 는 배열이 채워질 때 배열에서 정수를 가져옵니다. 순서에 대한 통지 thread2 를 작동 할 수는 있는지, 배열의 상태를 알 필요가 thread1 그것을 채워지거나하지 않았다.

구현 Runnable하면 객체를 공유 할 수있는 유연성을 가지면서 extends Thread각 스레드에 대해 새 객체를 만들 수 있으므로 thread1이 수행 한 모든 업데이트가 thread2에 손실됩니다.


7

내가 틀리지 않으면, 그것은 다소 비슷합니다.

인터페이스와 추상 클래스의 차이점은 무엇입니까?

extends는 " Is A "관계를 설정하고 인터페이스는 " Has "기능을 제공합니다.

Runnable 구현을 선호하십시오 .

  1. Thread 클래스를 확장하고 Thread API 기본 구현을 수정할 필요가없는 경우
  2. 화재를 실행하고 명령을 잊어 버린 경우
  3. 이미 다른 수업을 연장하고 있다면

" 스레드 확장 "선호 :

  1. Oracle 설명서 페이지에 나열된 이러한 스레드 방법 을 재정의해야하는 경우

일반적으로 스레드 동작을 재정의 할 필요는 없습니다. 그래서 Runnable를 구현한다 대부분의 경우 이 선호됩니다.

다른 말로, 고급 ExecutorService또는 ThreadPoolExecutorServiceAPI를 사용하면 더 많은 유연성과 제어 기능을 제공합니다.

이 SE 질문을 살펴보십시오.

ExecutorService 및 캐주얼 스레드 생성기


5

Runnable 구현에서 Thread 클래스를 분리하면 스레드와 run () 메소드 간의 잠재적 동기화 문제를 피할 수 있습니다. 별도의 Runnable은 일반적으로 실행 가능한 코드를 참조하고 실행하는 방식에서 더 큰 유연성을 제공합니다.


5

이것이 바로 SOLIDS 입니다 . 단일 책임.

스레드 구체화 실행 컨텍스트 의 (스택 프레임 스레드 ID 등의 실행 환경에서와 같이) 비동기 실행 코드의 조각. 해당 코드동기식 이든 비동기식 이든 동일한 구현이 이상적 입니다.

하나의 구현으로 함께 묶으면 결과 객체에 두 가지 관련없는 변경 원인이 있습니다.

  1. 응용 프로그램에서 스레드 처리 (예 : 실행 컨텍스트 쿼리 및 수정)
  2. 코드 조각에 의해 구현되는 알고리즘 (실행 가능한 부분)

사용하는 언어가 부분 클래스 또는 다중 상속을 지원하는 경우 각 원인을 자체 수퍼 클래스로 분리 할 수 ​​있지만 기능 세트가 겹치지 않기 때문에 두 객체를 구성하는 것과 동일하게 요약됩니다. 그것은 이론을위한 것입니다.

실제로, 일반적으로 말해서, 프로그램은 필요 이상으로 더 복잡한 것을 수행 할 필요가 없습니다. 특정 작업을 수행하는 스레드가 하나만 있으면 해당 작업을 변경하지 않고도 작업을 별도의 클래스로 만들 필요가 없으며 코드가 더 간단하게 유지됩니다.

의 맥락에서 자바 시설이기 때문에, 이미 , 독립적으로 직접 시작하는 것이 더 쉬울 것입니다 Runnable클래스 및 자신의 인스턴스를 통과 Thread(또는 Executor) 인스턴스. 해당 패턴에 익숙해 지면 간단한 실행 가능한 스레드 케이스보다 사용하기가 어렵거나 읽기가 어렵지 않습니다.


5

기본 클래스를 확장하는 대신 인터페이스를 구현하려는 한 가지 이유는 이미 다른 클래스를 확장하고 있기 때문입니다. 하나의 클래스 만 확장 할 수 있지만 여러 인터페이스를 구현할 수 있습니다.

Thread를 확장하면 기본적으로 'this'이외의 다른 스레드에서 논리를 실행하지 못하게됩니다. 일부 스레드 에서만 논리를 실행하려면 Runnable을 구현하는 것이 좋습니다.


예. Runnable 인터페이스를 구현하여 클래스를 확장하여 자신의 논리를 자유롭게 구현할 수 있으므로 Runnable이 Thread 클래스보다 선호됩니다.
Akash5288

5

실행 가능을 사용하는 경우 다른 클래스로 확장 할 공간을 절약 할 수 있습니다.


5

클래스가 행동하기를 원하는 기본 이유를 다시 방문 할 수 있습니까 Thread? 전혀 이유가 없습니다. 우리는 비동기 모드에서 작업을 실행하고 싶었습니다. 정확히 작업이 실행되면 기본 스레드와 기본 스레드에서 분기되어야하며 조기에 완료되면 대기하거나 대기하지 않을 수 있습니다 분기 경로 (작업).

이것이 전체 목적이라면 특수 스레드가 필요한 곳은 어디입니까? 이것은 시스템의 스레드 풀에서 RAW 스레드를 선택하여 태스크 (클래스의 인스턴스 일 수 있음)를 지정하여 수행 할 수 있습니다.

OOP 개념을 따르고 필요한 유형의 클래스를 작성해 봅시다. 올바른 방법으로 작업을 수행하는 방법에는 여러 가지가 있습니다.

작업이 필요하므로 스레드에서 실행할 수있는 작업 정의를 작성하십시오. Runnable을 사용하십시오.

항상 implements행동을 전하기 위해 사용된다는 것을 항상 기억하십시오.extends 을 부여하는 데 사용되며 기능 / 재산을 부여하는 데 사용됩니다.

우리는 스레드의 속성을 원하지 않고 대신 클래스가 실행될 수있는 작업으로 동작하기를 원합니다.


4

예, ThreadA call을 호출하면 start 메소드를 호출 할 필요가 없으며 ThreadA 클래스 만 호출 한 후 run 메소드가 호출됩니다. 그러나 ThreadB 호출을 사용하는 경우 호출 실행 메소드의 시작 스레드가 필요합니다. 더 도움이 필요하면 답장 해주세요.


4

언급 한 모든 이유로 Runnable을 사용하는 것이 가장 유용하다는 것을 알지만 때로는 스레드를 확장하여 자체 스레드 중지 메소드를 작성하고 작성한 스레드에서 직접 호출 할 수 있습니다.


4

Java는 다중 상속을 지원하지 않으므로 Thread 클래스를 확장하면 다른 클래스가 확장되지 않습니다.

예를 들어 : 애플릿을 작성하는 경우 애플릿 클래스를 확장해야하므로 여기에서 스레드를 작성하는 유일한 방법은 Runnable 인터페이스를 구현하는 것입니다.


4

Runnable인터페이스이며이 인터페이스 Thread를 구현하는 클래스입니다. 디자인 관점에서 작업을 정의하는 방법과 실행 방법을 명확하게 분리해야합니다. 전자는 Runnalbe구현 의 책임이며 후자는 Thread클래스의 역할입니다. 대부분의 경우 구현 Runnable이 올바른 방법입니다.


4

Thread와 runnable의 차이점 Thread 클래스를 사용하여 Thread를 생성하는 경우 생성 한 객체 수와 동일한 Thread 수입니다. 실행 가능한 인터페이스를 구현하여 스레드를 만드는 경우 단일 스레드를 사용하여 여러 스레드를 만들 수 있습니다. 따라서 단일 개체는 여러 스레드에서 공유되므로 메모리가 적게 걸립니다.

우리의 데이터가 민감하지 않은 경우 요구 사항에 따라. 따라서 Runnable 인터페이스를 사용할 수있는 여러 스레드간에 공유 할 수 있습니다.


4

여기에 내 두 센트를 추가- 가능하면 항상 사용하십시오 implements Runnable. 다음은 extends Threads를 사용하지 말아야 할 두 가지 경고 사항입니다.

  1. 이상적으로 Thread 클래스를 확장해서는 안됩니다. Thread클래스해야한다 final. 적어도 그와 같은 방법 thread.getId(). s 확장과 관련된 버그에 대해서는 토론을 참조하십시오 Thread.

  2. 퍼즐을 풀고 싶은 사람들은 스레드를 확장하는 또 다른 부작용을 볼 수 있습니다. 아래 코드는 아무도 알리지 않으면 도달 할 수없는 코드를 인쇄합니다.

http://pastebin.com/BjKNNs2G를 참조 하십시오 .

public class WaitPuzzle {

    public static void main(String[] args) throws InterruptedException {
        DoNothing doNothing = new DoNothing();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        Thread.sleep(100);
        doNothing.start();
        while(true) {
            Thread.sleep(10);
        }
    }


    static class WaitForever extends  Thread {

        private DoNothing doNothing;

        public WaitForever(DoNothing doNothing) {
            this.doNothing =  doNothing;
        }

        @Override
        public void run() {
            synchronized (doNothing) {
                try {
                    doNothing.wait(); // will wait forever here as nobody notifies here
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Unreachable Code");
            }
        }
    }

    static class DoNothing extends Thread {

        @Override
        public void run() {
            System.out.println("Do Nothing ");
        }
    } 
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.