[java] Java 스레드 프로그래밍

Java는 멀티스레드 프로그래밍을 지원하는 언어입니다. 스레드는 프로세스 내에서 독립적으로 실행되는 작업 단위이며, 동시에 여러 작업을 처리할 수 있습니다. 이 글에서는 Java에서 스레드를 사용하는 방법과 스레드를 이용한 병렬 처리에 대해 알아보겠습니다.

스레드 생성하기

Java에서 스레드를 생성하는 가장 간단한 방법은 Thread 클래스를 상속하고 run 메서드를 오버라이딩하는 것입니다. 다음은 이러한 예시입니다.

public class MyThread extends Thread {
  @Override
  public void run() {
    // 스레드에서 실행될 코드 작성
  }
}

public class Main {
  public static void main(String[] args) {
    MyThread thread = new MyThread();
    thread.start();
  }
}

위의 예시에서 MyThread 클래스는 Thread 클래스를 상속하고, run 메서드를 오버라이딩하여 스레드에서 실행될 코드를 작성합니다. Main 클래스에서 MyThread 객체를 생성하고 start 메서드를 호출하여 스레드를 시작합니다.

Runnable 인터페이스 사용하기

또 다른 스레드 생성 방법은 Runnable 인터페이스를 구현하는 것입니다. 이 방법은 다중 상속이 불가능한 Java에서 유용하게 사용됩니다. 다음은 이러한 예시입니다.

public class MyRunnable implements Runnable {
  @Override
  public void run() {
    // 스레드에서 실행될 코드 작성
  }
}

public class Main {
  public static void main(String[] args) {
    MyRunnable myRunnable = new MyRunnable();
    Thread thread = new Thread(myRunnable);
    thread.start();
  }
}

위의 예시에서 MyRunnable 클래스는 Runnable 인터페이스를 구현하고, run 메서드를 오버라이딩하여 스레드에서 실행될 코드를 작성합니다. Main 클래스에서 MyRunnable 객체를 생성하고 Thread 클래스에 MyRunnable 객체를 전달하여 스레드를 생성하고 시작합니다.

스레드 동기화(Synchronization)

여러 스레드가 공유 자원에 동시에 접근하는 경우, 스레드 동기화가 필요합니다. Java에서 제공하는 synchronized 키워드를 사용하여 스레드 동기화를 구현할 수 있습니다. 다음은 스레드 동기화의 예시입니다.

public class Counter {
  private int count = 0;

  public synchronized void increment() {
    count++;
  }
}

public class Main {
  public static void main(String[] args) {
    Counter counter = new Counter();

    Thread thread1 = new Thread(() -> {
      for (int i = 0; i < 1000; i++) {
        counter.increment();
      }
    });

    Thread thread2 = new Thread(() -> {
      for (int i = 0; i < 1000; i++) {
        counter.increment();
      }
    });

    thread1.start();
    thread2.start();

    // 모든 스레드가 작업을 완료할 때까지 대기
    try {
      thread1.join();
      thread2.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    System.out.println("결과: " + counter.getCount());
  }
}

위의 예시에서 Counter 클래스는 count 변수를 가지고 있으며, increment 메서드는 synchronized 키워드를 통해 동기화됩니다. Main 클래스에서 Counter 객체를 생성하고 스레드를 생성하여 increment 메서드를 호출합니다. 두 스레드가 동시에 increment 메서드에 접근하지만, 동기화에 의해 정확한 결과를 얻을 수 있습니다.

스레드 풀(Thread Pool)

자바에서는 스레드 풀(Thread Pool)을 사용하여 스레드를 관리할 수 있습니다. 스레드 풀은 미리 생성된 일정한 수의 스레드를 재사용함으로써 스레드 생성 및 제거에 따른 오버헤드를 방지합니다. 다음은 스레드 풀을 사용하는 예시입니다.

ExecutorService executor = Executors.newFixedThreadPool(10);

for (int i = 0; i < 100; i++) {
  Runnable task = new MyTask();
  executor.submit(task);
}

executor.shutdown();

위의 예시에서 ExecutorService 인터페이스를 사용하여 스레드 풀을 생성합니다. newFixedThreadPool 메서드를 사용하여 스레드 풀의 크기를 지정합니다. submit 메서드를 호출하여 실행할 작업을 스레드 풀에 전달합니다. shutdown 메서드를 호출하여 스레드 풀을 종료합니다.

결론

Java에서 스레드를 사용하는 방법과 스레드를 이용한 병렬 처리에 대해 알아보았습니다. 스레드를 효과적으로 활용하여 동시성 문제를 해결하고 성능을 향상시킬 수 있습니다. 그러나 올바른 스레드 동기화와 스레드 풀 사용에 유의하여야 합니다. 추가적인 정보는 Java 공식 문서를 참고하시기 바랍니다.