티스토리 뷰

우리가 개발하는 WAS는 여러 개의 스레드가 동작하도록 되어 있다. 그래서 synchronized를 자주 사용한다. 하지만 synchonized를 쓴다고 무조건 안정적인 것은 아니며, 성능에 영향을 미치는 부분도 있다.

자바에서 스레드는 어떻게 사용하나?

 

프로세스와 스레드


클래스를 하나 수행시키거나 WAS를 기동 하면 서버에 자바 프로세스가 하나 생성된다. 하나가 생성되는지 여러 개가 생성되는지는 윈도우, 리눅스, 유닉스의 프로세스를 조회해 보면 확인 가능하다.

하나의 프로세스에는 여러 개의 스레드가 생성된다. 단일 스레드가 생성되어 종료될 수도 있고, 여러 개의 스레드가 생성되어 수행될 수도 있다. 그러므로 프로세스와 스레드의 관계는 1:N 관계라고 보면 된다. 스레드는 다른 말로 Light Weight Process(LWP)라고도 한다. 즉 가벼운 프로세스이고, 프로세스에서 만들어 사용하고 있는 메모리를 공유한다.

 

Thread 클래스 상속과 Runnable 인터페이스 구현


스레드의 구현은 Thread 클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법 두 가지가 있다. 기본적으로 Thread 클래스는 Runnable 인터페이스를 구현한 것이기 때문에 어느 것을 사용해도 거의 차이가 없다. 대신 Runnable 인터페이스를 구현하면 원하는 기능을 추가할 수 있다. 이는 장점이 될 수도 있지만, 해당 클래스를 수행할 때 별도의 스레드 객체를 생성해야 한다는 점은 단점이 될 수도 있다. 또한 자바는 다중 상속을 인정하지 않는다. 따라서 스레드를 사용해야 할 때 이미 상속받은 클래스가 존재한다면 Runnable 인터페이스를 구현해야 한다.


public class ThreadExtends extends Thread {
    public void run() {
        System.out.println("This is ThreadExtends");
    }
}

public class RunnableImpl implements Runnable {
    public void run() {
    	System.out.println("This is RunnableImpl");
    }
}

Thread 클래스를 상속받는 경우에는 start() 메서드를 호출하면 된다. 하지만 Runnable 인터페이스를 구현한 경우에는 Thread 클래스의 Runnable 인터페이스를 매개변수로 받는 생성자를 사용해서 Thread 클래스를 만든 후 start() 메서드를 호출해야 한다.


public class RunThread {
    public static void main(String []args) {
        ThreadExtends thread = ThreadExtends();
        thread.start();
        RunnableImpl runnable = RunnableImpl();
        new Thread(runnable).start();
    }
}

sleep(), wait(), join() 메서드


현재 진행 중인 스레드를 대기하도록 하기 위해서는 sleep(), wait(), join() 세 가지 메서드를 사용하는 방법이 있다. 이 세 가지 메서드는 모두 예외를 던지도록 되어 있어 사용할 때는 반드시 예외 처리를 해주어야 한다.

sleep() - 명시된 시간만큼 해당 스레드를 대기시킨다.

wait() - 명시된 시간만큼 해당 스레드를 대기시킨다. 매개변수를 지정하지 않으면 notify() 메서드 혹은 notifyAll() 메서드가 호출될 때까지 대기한다.

join() - 명시된 시간만큼 해당 스레드가 죽기를 기다린다.

 

interrupt(), notify(), notifyAll() 메서드


interrupt() - 앞서 명시한 세 개의 메서드를 멈출 수 있는 유일한 메서드이다. interrupt() 메서드가 호출되면 중지된 스레드에는 InterruptedException이 발생한다.

notify() - wait() 메서드를 멈추기 위한 메서드이다. 객체의 모니터와 관련있는 단일 스레드를 깨운다.

notifyAll() - wait() 메서드를 멈추기 위한 메서드이다. 객체의 모니터와 관련있는 모든 스레드를 깨운다.

 

 

synchronized


메서드를 동기화하여면 메서드 선언부에 사용하고, 특정 부분을 동기화하려면 해당 블록에 선언하여 사용하면 된다. 그럼 언제 동기화를 사용해야 하는지 알아보자.

  • 하나의 객체를 여러 스레드에서 동시에 사용할 경우
  • static으로 선언한 객체를 여러 스레드에서 동시에 사용할 경우

간단하게 두가지로 요약할 수 있다. 다시말로는 위와 같은 경우가 아니면 동기화를 할 필요가 별로 없다.

synchronized는 여러 스레드에서 접근하는 것을 막아주는 장점이 있지만, 성능 저하가 발생한다는 단점이 있다. 단지 메서드가 중요하다고 synchronized로 선언하지 않기를 바란다.

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함