Java는 멀티스레딩(Multi-threading)과 동시성(Concurrency) 프로그래밍을 지원합니다. 그리고 대규모 애플리케이션의 성능을 향상 시킬 수 있습니다. 멀티스레딩을 활용하면 여러 작업을 병렬로 수행하여 응답성을 개선하고, CPU 사용률을 높일 수 있습니다.
이번 포스팅에서는 Java의 멀티스레딩 기법과 동시성 프로그래밍의 핵심 개념, 그리고 실무에서 활용할 수 있는 최적화 기법을 알려 드리겠습니다.
1. 멀티스레딩과 동시성의 차이
Java에서는 Thread
클래스를 상속하거나 Runnable
인터페이스를 구현하여 멀티스레딩을 구현이 가능합니다.
2. Java에서의 멀티스레딩 구현
1) Thread 클래스 상속
class MyThread extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
2) Runnable 인터페이스 구현
class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
Runnable
을 사용하면 다중 상속 문제를 피할 수 있으며, 더 유연한 코드 구성도 가능하며, 활용도가 높습니다.
3. ExecutorService를 이용한 Thread 관리
Java에서는 ExecutorService
를 사용하여 Thread를 효율적으로 관리할 수 있습니다.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " is executing");
});
}
executor.shutdown();
}
}
newFixedThreadPool(n)
: 고정된 개수의 Thread 풀을 생성 합니다.newCachedThreadPool()
: 필요에 따라 동적으로 Thread를 생성합니다.newSingleThreadExecutor()
: 단일 Thread를 사용하여 순차적으로 작업을 처리를 합니다.4. 동기화(Synchronization)와 경쟁 상태 해결
멀티스레딩 환경에서는 여러 Thread가 공유 자원에 동시에 접근하면 경쟁 상태(Race Condition) 문제가 발생할 수 있으며, 이를 해결하기 위해 동기화 기법을 사용합니다.
1) synchronized 키워드 사용
class SharedResource {
synchronized void printMessage(String message) {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ": " + message);
}
}
}
public class SyncExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread t1 = new Thread(() -> resource.printMessage("Hello"));
Thread t2 = new Thread(() -> resource.printMessage("World"));
t1.start();
t2.start();
}
}
2) ReentrantLock 사용
synchronized
의 단점을 보완하기 위해 ReentrantLock
을 사용할 수 있습니다.
import java.util.concurrent.locks.ReentrantLock;
class SharedResource {
private final ReentrantLock lock = new ReentrantLock();
void printMessage(String message) {
lock.lock();
try {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ": " + message);
}
} finally {
lock.unlock();
}
}
}
5. 동시성 프로그래밍을 위한 고급 기법
1) CompletableFuture 활용
비동기 작업을 쉽게 관리할 수 있도록 CompletableFuture
를 사용할 수 있습니다.
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept(System.out::println);
}
}
2) Atomic 변수 활용 (CAS 알고리즘)
멀티스레드 환경에서 synchronized
없이 동기화를 수행할 수 있습니다.
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static final AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(counter.incrementAndGet());
}).start();
}
}
}
6. 멀티스레딩 및 동시성 프로그래밍의 모범 사례
ThreadLocal
을 활용할 수 있습니다.ExecutorService
를 사용하여 스레드 생성 비용 절감할 수 있습니다.synchronized
대신 ConcurrentHashMap
, Atomic
클래스를 활용할 수 있습니다.tryLock()
을 활용하여 교착 상태 방지할 수 있습니다.결론
Java의 멀티스레딩과 동시성 프로그래밍은 성능을 극대화할 수 있는 강력한 Tool입니다. Thread
, ExecutorService
, CompletableFuture
등을 활용하게 된다면 효율적인 병렬 처리를 구현할 수 있습니다. 최적화된 멀티스레드 애플리케이션을 한번 Test 해보시길 바랍니다. 감사합니다.
디딤돌 전세 대출을 받은 실제 경험 바탕으로 포스팅을 작성합니다. 디딤돌 대출은 생애최초, 신혼부부 대출로 나뉘어져…
현대 웹 애플리케이션에서는 보안이 중요한 요소이며, Spring Security는 강력한 인증 및 권한 관리 기능을 제공합니다.…
애플리케이션 개발에서 로깅(logging)과 모니터링(monitoring)은 필수적인 요소입니다. Java 애플리케이션이 실행되는 동안 발생하는 이벤트를 기록하고, 성능 및…
API(Application Programming Interface)는 애플리케이션 간 데이터를 주고받기 위한 인터페이스로, Java에서는 주로 RESTful API와 GraphQL API를…
디자인 패턴(Design Patterns)은 소프트웨어 개발에서 자주 발생하는 문제를 해결하기 위한 일반적인 해결책을 제공합니다. 특히, GoF(Gang…
Java의 애너테이션(Annotation)은 메타데이터를 코드에 추가하는 기능을 제공합니다. 이를 활용하면 코드의 가독성을 높이고, 프레임워크에서 런타임 처리를…