[java] 스레드 간의 통신(Communication)과 데이터 공유

여러 개의 스레드가 동시에 실행되는 프로그램에서는 스레드 간의 통신과 데이터 공유가 중요한 요소입니다. 스레드 간에 데이터를 주고받을 때는 정확하고 안전한 방법을 사용해야 합니다. 이번 포스트에서는 자바를 예로 들어 스레드 간의 통신과 데이터 공유에 대해 알아보겠습니다.

1. 스레드 간의 통신

스레드 간의 통신은 스레드들이 서로 데이터를 주고받는 메커니즘을 의미합니다. 자바에서는 스레드 간의 통신을 위해 다음과 같은 방법을 사용할 수 있습니다.

1.1. 공유 변수를 이용한 통신

스레드 간 통신을 위해 가장 간단한 방법은 공유 변수를 사용하는 것입니다. 여러 스레드가 하나의 변수를 읽고 쓰는 방식으로 데이터를 주고받을 수 있습니다. 하지만 이 방법은 스레드 간 동기화 문제를 고려해야 한다는 문제가 있습니다.

public class SharedVariableExample {
    private int sharedVariable = 0;

    public void increaseSharedVariable() {
        synchronized (this) {
            sharedVariable++;
        }
    }

    public int getSharedVariable() {
        synchronized (this) {
            return sharedVariable;
        }
    }
}

위의 예시에서는 sharedVariable이 공유 변수로 사용되고 있습니다. increaseSharedVariable() 메서드는 sharedVariable 값을 증가시키고, getSharedVariable() 메서드는 sharedVariable 값을 반환합니다. 이때 synchronized 키워드를 사용하여 스레드 간 동기화를 구현하고 있습니다.

1.2. 메시지 큐를 이용한 통신

메시지 큐는 스레드 간 통신을 위해 사용되는 자료구조로, 한 스레드가 메시지를 생성하고 다른 스레드가 해당 메시지를 수신하는 방식입니다. 메시지 큐를 사용하면 스레드 간의 동기화 문제를 처리하기 쉬워지는 장점이 있습니다.

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class MessageQueueExample {
    private BlockingQueue<String> messageQueue = new LinkedBlockingQueue<>();

    public void addMessage(String message) {
        messageQueue.add(message);
    }

    public String getMessage() throws InterruptedException {
        return messageQueue.take();
    }
}

위의 예시에서는 messageQueue라는 메시지 큐를 사용하여 스레드 간 통신을 구현하고 있습니다. addMessage() 메서드는 메시지 큐에 메시지를 추가하고, getMessage() 메서드는 메시지 큐에서 메시지를 가져옵니다. 이때 BlockingQueue 인터페이스를 사용하므로써 스레드 간의 동기화가 자동으로 처리됩니다.

2. 데이터 공유

여러 개의 스레드가 공유 데이터에 접근할 때는 동기화 문제가 발생할 수 있습니다. 자바에서는 다음과 같은 방법을 사용하여 데이터 공유를 안전하게 처리할 수 있습니다.

2.1. synchronized 키워드

synchronized 키워드를 사용하여 스레드 간의 동기화 문제를 해결할 수 있습니다. synchronized 키워드를 사용하는 방법은 위의 스레드 간 통신 예시에서 이미 다루었습니다.

2.2. Lock 객체

java.util.concurrent.locks 패키지에는 동기화를 위한 Lock 인터페이스와 그 구현체들이 제공됩니다. Lock 객체를 사용하면 더 세밀한 제어가 가능해지고, 조건 변수를 사용하여 스레드의 대기와 통지를 관리할 수 있습니다.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DataSharingExample {
    private int sharedData = 0;
    private Lock lock = new ReentrantLock();

    public void incrementSharedData() {
        lock.lock();
        try {
            sharedData++;
        } finally {
            lock.unlock();
        }
    }

    public int getSharedData() {
        lock.lock();
        try {
            return sharedData;
        } finally {
            lock.unlock();
        }
    }
}

위의 예시에서는 sharedData라는 공유 데이터를 ReentrantLock 객체인 lock을 사용하여 동기화하고 있습니다. incrementSharedData() 메서드와 getSharedData() 메서드에서는 lock() 메서드를 호출하여 잠금을 획득하고, 작업이 끝나면 unlock() 메서드를 호출하여 잠금을 해제합니다.

결론

스레드 간의 통신과 데이터 공유는 멀티스레드 프로그래밍에서 중요한 요소입니다. 자바에서는 공유 변수를 이용한 통신과 메시지 큐를 이용한 통신으로 스레드 간 데이터를 주고받을 수 있으며, synchronized 키워드와 Lock 객체를 사용하여 데이터 공유를 안전하게 처리할 수 있습니다. 스레드 간의 통신과 데이터 공유를 올바르게 구현하여 프로그램의 정확성과 성능을 보장할 수 있습니다.

참고 자료