[java] Java에서 “실행 가능 구현”과 “스레드 확장”

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

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



답변

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

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


답변

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());


답변

이야기의 교훈:

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

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

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


답변

좋은 답변이 너무 많아서 더 추가하고 싶습니다. 이것은 이해하는 데 도움이됩니다 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 인터페이스를 구현하는 동안 다른 클래스를 확장하여 스레드를 만들 수 있기 때문입니다.

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


답변

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

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


답변

다른 클래스를 구현하거나 확장하려면 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, 그것은 여러 스레드에 동일한 개체를 공유합니다.


답변

사실, 비교하는 것이 현명하지 않다 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경쟁하지 또는 교체를 멀티 스레딩을 위해 서로 보완된다.