Thread-safe란 자바에서의 동기화 처리

Y_Sevin·2023년 3월 14일
0

멀티스레드를 효과적으로 사용하면 프로그램 성능을 크게 향상시킬 수 있지만, 여러 스레드가 동시에 데이터를 공유하고 수정할 때 발생하는 동기화 문제를 해결하지 않으면 데이터의 안정성과 신뢰성을 보장할 수 없습니다.
자바에서는 이러한 문제를 해결하기 위해 synchronized 키워드를 제공하여 스레드 간 동기화를 가능하게 합니다.

스레드 간 동기화를 왜 해야할까?

멀티스레드 환경에서 공유 데이터를 안전하게 관리하려면 스레드 간 동기화가 필수적입니다. 예를 들어, 여러 스레드가 동일한 데이터에 접근하고 수정할 때 동기화가 이루어지지 않으면 데이터의 일관성이 깨질 수 있습니다. 이러한 문제를 해결하기 위해 자바는 synchronized 키워드를 제공합니다.

자바에서의 synchronized 키워드

synchronized 키워드는 여러 스레드가 하나의 자원을 사용하고자 할 때, 현재 자원을 사용 중인 스레드를 제외하고 나머지 스레드의 접근을 막는 역할을 합니다. 이는 데이터의 일관성을 유지하고 스레드 간의 경합 조건을 방지하는 데 유용합니다. 하지만, synchronized 키워드를 남용하면 오히려 프로그램의 성능 저하를 초래할 수 있으므로 적절한 사용이 중요합니다.

synchronized

1. 메서드에 사용하는 경우

public synchronized void method() {
    // 코드
}

메서드에 synchronized 키워드를 사용하면 해당 메서드를 실행하는 동안 다른 스레드가 이 메서드에 접근하지 못하게 합니다.

2. 블록에 사용하는 경우

private final Object lock = new Object();

public void exampleMethod() {
    synchronized(lock) {
        // 코드
    }
}

동기화 블록을 사용하여 특정 객체를 기준으로 동기화할 수 있습니다. 이 방법은 메서드 전체가 아닌 특정 부분만 동기화할 때 유용합니다.

동기화 처리 전 코드

public class ThreadSynchronizedTest {
    public static void main(String[] args) {
        Task task = new Task();
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        
        t1.setName("t1-Thread");
        t2.setName("t2-Thread");
        
        t1.start();
        t2.start();
    }
}

class Account {
    int balance = 1000;

    public void withDraw(int money) {
        if (balance >= money) {
            try {
                Thread thread = Thread.currentThread();
                System.out.println(thread.getName() + " 출금 금액 ->> " + money);
                Thread.sleep(1000);
                balance -= money;
                System.out.println(thread.getName() + " balance: " + balance);
            } catch (Exception e) {}
        }
    }
}

class Task implements Runnable {
    Account acc = new Account();

    @Override
    public void run() {
        while (acc.balance > 0) {
            int money = (int) (Math.random() * 3 + 1) * 100;
            acc.withDraw(money);
        }
    }
}

위 예제에서 두 스레드 t1t2가 동시에 balance를 감소시키는 작업을 수행합니다. 그러나 동기화가 이루어지지 않아 balance 값이 마이너스가 되는 문제가 발생할 수 있습니다.

동기화 처리 후 코드

public class ThreadSynchronizedTest {
    public static void main(String[] args) {
        Task task = new Task();
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        
        t1.setName("t1-Thread");
        t2.setName("t2-Thread");
        
        t1.start();
        t2.start();
    }
}

class Account {
    int balance = 1000;

    public synchronized void withDraw(int money) {
        if (balance >= money) {
            try {
                Thread thread = Thread.currentThread();
                System.out.println(thread.getName() + " 출금 금액 ->> " + money);
                Thread.sleep(1000);
                balance -= money;
                System.out.println(thread.getName() + " balance: " + balance);
            } catch (Exception e) {}
        }
    }
}

class Task implements Runnable {
    Account acc = new Account();

    @Override
    public void run() {
        while (acc.balance > 0) {
            int money = (int) (Math.random() * 3 + 1) * 100;
            acc.withDraw(money);
        }
    }
}

동기화 키워드를 사용하여 withDraw 메서드를 동기화하면, 하나의 스레드가 이 메서드를 실행하는 동안 다른 스레드는 접근할 수 없습니다. 이를 통해 balance 값의 일관성을 유지할 수 있습니다.

결론

멀티스레드 환경에서 동기화는 필수적인 요소입니다. 자바의 synchronized 키워드를 사용하여 스레드 간의 경합을 방지하고 데이터의 일관성을 유지할 수 있습니다.
하지만, synchronized 키워드의 남용은 성능 저하를 초래할 수 있으므로, 적재적소에 사용해야 한다는 특징이 있습니다. 때문에 데이터의 일관성을 꼭 보장해야할 필요가 없다면 사용하지 않는 것이 좋을때도 있습니다.

profile
매일은 아니더라도 꾸준히 올리자는 마음으로 시작하는 개발블로그😎

0개의 댓글