본문 바로가기
Programming/Java

[자바/Java] Synchronized

by 코딩하는 랄로 2023. 11. 16.
728x90

Synchronized

synchronized는 '동기화'를 의미한다. 자바에서 말하는 동기화란 무엇일까?

 

프로그램은 대부분 멀티 쓰레드를 사용하여 성능을 이끌어낸다. 여러 개의 쓰레드가 동시에 여러 작업들을 진행해주므로 단일 쓰레드에 비해 작업 효율면에서 큰 이점을 가지기 때문이다.

 

이러한 멀티 쓰레드 환경에서 주의할 점은 여러 쓰레드가 한 자원을 사용하는 경우에도 해당 자원의 안정성과 신뢰성을 보장해주어야 한다는 것이다. 그렇기 때문에 특정 쓰레드가 자원을 사용하고 있는 경우에, 다른 쓰레드가 해당 자원에 접근하지 못하도록 막는 것이 필요하다.

 

이 때 사용되는 것이 바로 synchronized, 동기화인것이다. 즉, 하나의 자원을 사용하기 위해 여러 쓰레드들을 조화시켜 충돌하지 않도록 하는 것이다. 

 

 

 

Synchronized 사용

자바에서 synchronized를 사용하는 방법에 는 메서드 앞에 선언하는 방식과 동기화 블럭을 만들어 사용하는 방식이 존재한다. 각각에 대해서 알아보기 전에, 먼저 동기화를 사용하지 않는 간단한 코드 예제를 통해 어떠한 문제점이 발생하는지 살펴보자.

package threadEx;

public class ThreadEx extends Thread{

    public static void main(String[] args) {
        Count cnt = new Count();
        for(int i = 0; i < 100; i++) {
            new Thread(() -> {
                for(int j = 0; j < 100; j++) {
                    System.out.println(cnt.cntInc());
                }
            }).start();
        }
        // 마지막 출력 : 9998
    }
}

class Count {
    private int cnt = 1;
    public int cntInc() { return cnt++; }
}

 

thread 두 개가 돌면서 num을 1씩 증가시키는 코드이다. 원하는 결과는 각각의 쓰레드가 1씩 증가를 십만번 반복하기 때문에 이십만이 나올꺼 같지만, 코드를 돌려보면 그렇지 않다는 것을 볼 수 있다.

 

해당 결과가 나오는 이유가 바로 공통된 자원에 접근하여 작업을 할 때, 동기화를 해주지 않았기 때문이다. 그렇기 때문에 위와 같은 동작 결과가 나오는 것이다. 이러한 결과를 막기 위해서 synchronized를 사용하여 동기화를 해주는 것이 필요하다.

 

 

메소드 synchronized

메소드 선언 부에 synchronized 를 선언해주는 방법이다. 이렇게 되면, 해당 메소드를 thread가 호출할 때 마다 다른 thread가 사용할 수 없게 막는다.

package threadEx;

public class ThreadEx extends Thread{

    public static void main(String[] args) {
        Count cnt = new Count();
        for(int i = 0; i < 100; i++) {
            new Thread(() -> {
                for(int j = 0; j < 100; j++) {
                    System.out.println(cnt.cntInc());
                }
            }).start();
        }
        // 마지막 출력 : 10000
    }
}

class Count {
    private int cnt = 1;
    public synchronized int cntInc() { return cnt++; }
}

 

 

 

synchronized block선언

또 다른 방식으로는 메소드가 아닌 synchronized block을 선언하여 동기화를 해주는 방법도 있다.

package threadEx;

public class ThreadEx extends Thread{

    public static void main(String[] args) {
        Count cnt = new Count();
        for(int i = 0; i < 100; i++) {
            new Thread(() -> {
                for(int j = 0; j < 100; j++) {
                    System.out.println(cnt.cntInc());
                }
            }).start();
        }
        // 마지막 출력 : 10000
    }
}

class Count {
    private int cnt = 1;
    public int cntInc() {
        synchronized (this) {
            return cnt++;
        }
    }
}

 

synchronized를 사용하면, 공유하는 자원에 대해 안전성과 신뢰성을 보장하여 주지만, 무분별하게 많이 사용하게 된다면 프로그램의 자원을 많이 잡아먹게 되어 역으로 프로그램이 느려지게 되기 때문에 필요한 경우에만 적절히 사용하여야 한다.

728x90