일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- mysql
- PostgreSQL
- 각데이터베이스별
- SQL
- 차이점
- 4요소
- Javascript
- 오블완
- 트랜잭션
- network
- 명령어
- c#특징
- 티스토리챌린지
- Python
- DBMS
- IntelliJ
- spring
- API
- git
- 뷰테이블
- analytics4
- oracle
- JPA
- mssql
- 자바
- java
- rebase vs merge
- docker
- group by
- Today
- Total
hanker
JAVA - 멀티스레딩 가이드 (구현부터 동기화까지) 본문
Java 멀티스레딩(Multi-threading)은 하나의 프로그램에서 동시에 여러 작업을 실행할 수 있도록 하는 강력한 기능이다.
이번 글에서 Java 멀티스레딩을 사용하는 방법과 주요 개념, 실 예제까지 알아보자!
1. 멀티스레딩의 정의와 필요성
멀티스레딩은 하나의 프로세스 내에서 여러 스레드(Thread)가 동시에 실행되도록 하는 기술이다.
Java는 기본적으로 멀티스레드 환경을 지원한다.
사용이점
1-1. 성능 향상 : 병렬 처리로 작업 시간을 줄일 수 있다.
1-2. 응답성(response time) 개선 : 긴 작업이 진행되는 동안에도 애플리케이션이 응답성을 유지할 수 있다.
1-3. 자원 효율성 : 동일한 메모리 공간에서 여러 작업을 수행하므로 자원을 더 효율적으로 사용할 수 있다.
2. Java의 스레드 모델
Java에서는 java.lang.Thread 클래스와 java.lang.Runnable 인터페이스를 사용하여 멀티스레드를 구현할 수 있다.
두 방식 모두 스레드의 실행 로직을 정의할 수 있지만, 상황에 따라 적합한 방법을 선택해야 한다.
2-1. Thread 클래스
Thread 클래스는 Java의 기본 스레드 구현체로, 직접 확장하여 스레드 동작을 정의할 수 있다.
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running: " + Thread.currentThread().getName());
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.start(); // 스레드 실행
}
}
2-2. Runnable 인터페이스
Runnable 인터페이스는 스레드 동작을 정의하는 또 다른 방법으로, 스레드를 생성하지 않고 실행 로직만을 분리할 수 있다.
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable is running: " + Thread.currentThread().getName());
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread thread1 = new Thread(new MyRunnable());
thread1.start();
}
}
3. 주요 멀티스레딩 기능
3-1. 스레드 우선순위 설정
스레드는 우선순위를 가질 수 있으며, 이는 운영체제가 스레드를 실행하는 데 참고한다.
기본값은 5이며, 최소값은 1, 최대값은 10이다.
Thread thread = new Thread(() -> System.out.println("Running"));
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
3-2. 스레드 동기화
멀티스레드 환경에서 여러 스레드가 동일한 자원을 접근하면 데이터 불일치 문제가 발생할 수 있다.
이를 방지하기 위해 synchronized 키워드를 사용한다.
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SynchronizationExample {
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("Final count: " + counter.getCount());
}
}
3-3. 스레드 간 통신
스레드 간의 데이터 교환이나 작업 조정을 위해 wait(), notify(), notifyAll() 메서드를 사용할 수 있다.
- wait() : 현재 스레드를 대기 상태로 전환. 다른 스레드가 notify(), notifyAll()을 호출하기 전까지 대기 상태에 있다.
- notify() : 대기 중인 스레드 중 하나를 실행시킨다.
- notifyAll() : 대기 중인 모든 스레드를 실행시킨다.
class SharedResource {
private boolean available = false;
public synchronized void produce() throws InterruptedException {
while (available) {
wait();
}
System.out.println("Producing...");
available = true;
notify();
}
public synchronized void consume() throws InterruptedException {
while (!available) {
wait();
}
System.out.println("Consuming...");
available = false;
notify();
}
}
public class CommunicationExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
resource.produce();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
resource.consume();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
}
}
4. 그 외 멀티스레딩 기능
4-1. Executor 프레임워크
Java 5부터 추가된 Executor 프레임워크는 스레드 생성을 간소화하고, 스레드 풀(Thread Pool)을 관리할 수 있는 기능을 제공한다.
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("Task executed by: " + Thread.currentThread().getName()));
}
executor.shutdown();
}
}
4-2 멀티스레드에 적합한 컬렉션
멀티스레드 환경에서 안전하게 사용할 수 있는 컬렉션 클래스는 ConcurrentHashMap, CopyOnWriteArrayList이다.
- ConcurrentHashMap
ConcurrentHashMap은 멀티스레드 환경에서 사용할 수 있도록 나온 HashMap의 스레드 안전 버전으로,
병렬 처리를 위해 내부적으로 여러 세그먼트로 나누어 동작한다. 이를 통해 여러 스레드가 동시에 읽고 쓸 수 있다.
- CopyOnWriteArrayList
CopyOnWriteArrayList는 읽기 작업이 빈번한 환경에서 안전하게 사용되는 리스트이다.
쓰기 작업이 발생할 때 내부 배열을 복사하여 동작한다.
정리
Java 멀티스레딩 기능은 올바르게 사용하지 않으면 디버깅이 어려운 문제를 초래할 수 있다.
기본적인 스레드 생성 방법 부터 동기화와 통신 기법, Executor 프레임워크까지 다양한 기능들을 숙지해서 올바른 멀티스레드 기능을 사용하길 바란다!
끝.
'JAVA' 카테고리의 다른 글
Java - CheckedException과 UncheckedException이 뭐지? (0) | 2024.12.06 |
---|---|
Java - 컴파일타임(Compile Time)과 런타임(Run Time)이 뭘까? (0) | 2024.12.05 |
Java - Java Servlet 이란? (0) | 2024.12.04 |
JAVA - JAVA에서 쓰레드란 무엇이며, 어떻게 생성할까? (0) | 2024.12.03 |
JAVA - JAVA에서 가비지 컬렉션(GC)이란 무엇일까? (0) | 2024.12.02 |