[Java] Runnable 인터페이스와 Thread 클래스 차이

민지·2023년 8월 10일
0

Java

목록 보기
9/20
post-custom-banner

쓰레드란 프로그램 실행의 가장 작은 단위이다. 일반적으로 자바 애플리케이션을 만들어 실행하면 1개의 메인(main) 쓰레드에 의해 프로그램이 실행된다. 하지만 1개의 쓰레드 만으로는 동시에 여러 작업을 할 수 없어서 별도의 쓰레드를 만들어 실행시켜줘야 하는데, 자바는 멀티 쓰레드 기반으로 동시성 프로그래밍을 지원하기 위한 방법들을 계속해서 발전시켜 왔다.

1. Runnable 인터페이스

public class ClientRequestHandler implements Runnable{
	public void run() {
    
    }
}

Runnbale 인터페이스는 1개의 메소드 만을 갖는 함수형 인터페이스이다. implements Runnable 을 통해서 Runnable 인터페이스를 구현할 수 있다. 추상 메서드 run을 반드시 구현해야 한다.

Thread도 run을 구현해야 한다는 점은 같지만 추상 메서드가 아니라 단순 메서드 오버라이딩으로 구현한다.

Runnable 인터페이스를 구현한 경우에는 다른 인터페이스를 추가로 구현할 수 있을 뿐만 아니라 다른 클래스도 상속받을 수 있다. Runnable 인터페이스를 구현하게되면 재사용성이 높고, 코드의 일관성을 유지할 수 있어서 Thread보다 더 효율적인 방법이라 할 수 있다.

2. Thread 클래스

Thread는 쓰레드 생성을 위해 Java에서 미리 구현해둔 클래스이다. Thread 클래스로 쓰레드를 구현하려면 상속을 받고 내부에서 run 메소드를 구현해야 한다.
자바는 다중 상속을 허용하지 않기 때문에 Thread 클래스를 바로 상속받는 경우 다른 클래스를 상속받지 못한다. 일반적으로는 Thread 클래스를 확장하기보다는 Runnable 인터페이스를 구현해서 스레드를 사용한다.

따라서 클래스의 확장성이 중요하다면 Runnable 인터페이스를 구현하는 것이 더 좋겠다.

WAS는 스레드가 사용자 요청이 들어왔을 때 별도의 스레드를 만들어서 해당 요청을 처리하도록 되어있을 것이다. 스레드는 생성될 때 마다 독립적인 스택 메모리 공간을 할당받는다. 메모리를 할당 받는 작업은 비싼 작업이다. 사용자의 요청이 있을 때 마다 스레드를 생성하게 된다면 동시 접속자가 많아 질 경우에 스레드는 계속해서 많이 늘어날거고 cpu와 메모리 사용량이 증가하게 되며 성능이 매우 떨어진다. 최악의 경우엔 서버가 감당하지 못해 다운 될 가능성도 있다.

스레드를 고정된 갯수만큼 생성해두고 이를 재활용하는 스레드 풀(Thread Pool) 을 사용해보자

private final ExecutorService excutorService = Executors.newFixedThreadPool(10);
excutorService.execute(new ClientRequestHandler(clientSocket));

실행해보면 pool-1thread-1 표시된 것을 확인해볼 수 있다.

Thread Synchronized (스레드 동기화)

멀티스레드 환경에서 하나의 객체를 여러 스레드에서 동시에 접근하여 사용하게 되면 Race Condition (: 여러 프로세스 혹은 스레드가 동시에 하나의 자원에 접근하기 위해 경쟁하는 것) 결과가 나타난다.

synchronized 키워드를 사용하여 여러 스레드가 동시에 접근하는 것을 금지함으로써 동기화를 할 수 있다.

  • 서블릿 객체는 싱글톤으로 관리 (인스턴스 하나만 생성하여 공유하는 방식)
  • 상태를 유지(stateful)하게 설계하면 안됨
  • Thread safety 하지 않음
public class Counter implements Runnable {

    private int count = 0;

    public void increment() {
        count++;
    }

    public void decrement() {
        count--;
    }

    public int getValue() {
        return count;
    }

    @Override
    public void run() {
        synchronized (this) { // 동기화 처리
            this.increment();
            System.out.println("Value for Thread After increment" + Thread.currentThread().getName() + " " + this.getValue());
            this.decrement();
            System.out.println("Value for Thread at last" + Thread.currentThread().getName() + " " + this.getValue());
        }
    }
}
profile
개발일지
post-custom-banner

0개의 댓글