JAVA 멀티스레딩(Multithreading)과 동기화(Synchronization)
1. 멀티스레딩이란?
멀티스레딩(Multithreading)은 하나의 프로세스 내에서 여러 개의 스레드를 실행하여 작업을 병렬로 수행하는 기술입니다. 이를 통해 CPU 활용도를 극대화하고 프로그램의 성능을 향상시킬 수 있습니다.
✅ 멀티스레딩의 주요 특징
- 병렬 처리 가능: 여러 작업을 동시에 수행하여 실행 속도 향상
- 리소스 공유: 하나의 프로세스 내에서 여러 스레드가 같은 메모리를 공유 가능
- 스레드 간 간섭 가능성: 동기화 문제 발생 가능
2. Java에서 스레드 생성 방법
2.1 Thread 클래스를 상속하는 방법
class MyThread extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 실행 중");
}
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
✅ Thread 클래스를 상속받아 run() 메서드를 오버라이딩하면 새로운 스레드를 생성할 수 있습니다.
2.2 Runnable 인터페이스를 구현하는 방법
class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 실행 중");
}
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.start();
thread2.start();
}
}
✅ Runnable 인터페이스를 구현하면 보다 유연하게 스레드를 활용할 수 있습니다.
3. 스레드 동기화(Synchronization)
멀티스레딩 환경에서는 여러 스레드가 동시에 공유 자원에 접근하면 충돌이 발생할 수 있습니다. 이를 방지하기 위해 **동기화(Synchronization)**를 적용해야 합니다.
3.1 synchronized 키워드 사용
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SynchronizedExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("최종 카운트: " + counter.getCount());
}
}
✅ synchronized 키워드를 사용하면 특정 메서드가 동시에 실행되지 않도록 보장할 수 있습니다.
3.2 ReentrantLock 사용
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
public class LockExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("최종 카운트: " + counter.getCount());
}
}
✅ ReentrantLock을 사용하면 synchronized보다 세밀한 제어가 가능합니다.
4. 스레드 풀(Thread Pool) 활용
스레드 풀은 필요할 때마다 새로운 스레드를 생성하는 대신, 미리 생성된 스레드를 재사용하여 성능을 최적화하는 기법입니다.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.submit(() -> System.out.println(Thread.currentThread().getName() + " 실행 중"));
}
executor.shutdown();
}
}
✅ ExecutorService를 활용하면 스레드의 생성과 종료를 효과적으로 관리할 수 있습니다.
5. 결론
이번 글에서는 Java의 멀티스레딩 개념과 동기화 기법에 대해 살펴보았습니다. Thread, Runnable, synchronized, ReentrantLock, 그리고 ExecutorService를 활용하여 멀티스레딩 환경에서 효율적인 동시 실행을 구현할 수 있습니다.
다음 글에서는 Java의 동시성(Concurrency)과 병렬성(Parallelism)의 차이점과 활용법에 대해 알아보겠습니다!