[Java] synchronized

N’oublie pas de t’aimer·2025년 1월 23일

Java

목록 보기
14/18

synchronize: 동시에 일어나다. 동시에 진행하다.

하나의 객체에 여러 요청이 동시에 달려들면 원하는 처리를 하지도 못하고 이상한 결과가 나올 수 있다. 그래서 synchronized를 사용해서 동기화를 하는 것이다. 이 식별자를 사용하면 "천천히 한 명씩 들어와!" 라고 해당 메서드나 블록에서 제어하게 된다.
즉 synchronized 키워드는 원자성 문제를 해결하기 위한 방법 중 하나이다.
synchronized는 lock을 이용해 동기화를 수행하며 4가지의 사용 방법이 존재한다.

  • synchronized method
  • static synchronized method
  • synchronized block
  • static synchronized block

synchronized를 static과 연결해서 생각해보면 더더욱 복잡해진다.
그러므로 일단 기본적인 부분부터 확인해보자.

synchronized는 다음과 같이 메서드와 블록으로 사용할 수 있다.
절대로 생성자의 식별자로는 사용할 수 없다.

public synchronized void sampleMethod() {
// 중간 생략
}
private Object obj = new Object();
public void sampleBlock() {
synchronized(obj) {
// 중간 생략
}
}

이처럼 간단히 synchronized라는 식별자만 쓰면 동기화할 수 있다. 사용 방법은 단순하지만, 이 식별자의 힘은 막강하다. 그럼 언제 동기화를 사용해야 할까?

  • 하나의 객체를 여러 스레드에서 동시에 사용할 경우
  • static으로 선언한 객체를 여러 스레드에서 동시에 사용할 경우

간단하게 두 가지로 요약할 수 있다. 거꾸로 이야기하면, 위의 경우가 아니면 동기화를 할 필요가 별로 없다.

동일 객체 접근 시

여러 기부자(Contributor)가 어떤 기부금을 처리하는 단체(Contribution)에 기부금을 내는 상황을 가정한다. 기부금을 내는 사람은 스레드로 구현되며, 기부금을 내는 사람의 이름 정보가 있어야 한다. 기부금을 받는 단체는 기부금을 받을 창구로 donate()라는 메서드를 제공한다. 기부한 전체 기부금을 확인하는 메서드는 getTotal()이다. 먼저 기부금을 받는 단체의 클래스를 구현한 소스를 보자.

public class Contribution {
	private int amount = 0;
    public void donate() {
    	amount++;
    }
    public int getTotal() {
    	return amount;
    }
}

기부금을 내는 사람의 클래스를 구현한 소스를 보자.

public class Contributor extends Thread {
	private Contribution myContribution;
    private String myName;
    public Contributor(Contribution contribution, String name) {
    	myContribution = contribution;
        myName = name;
    }
    public void run() {
    	for(int loop = 0; loop < 1000; loop++) {
        	myContribution.donate();
        }
        System.out.format("%s total=%d\n", myName, myContribution.getTotal());
    }
}

1인당 1원씩 1,000번 기부하고, 기부가 완료되면 현재까지 쌓인 기부금을 프린트하도록 되어 있다. 이제 기부를 하도록 하는 실행 파일을 보자.

public class ContributeTest {
	public static void main(String[] args) {
    	Contributor[] crs = new Contributor[10];
        // 기부자와 기부 단체 초기화
        for(int loop = 0; loop < 10; loop++) {
        	Contribution group = new Contribution();
           crs[loop] = new Contributor(group, " Contributor" + loop);
        }
        // 기부 실행
        for(int loop = 0; loop < 10; loop++) {
        	crs[loop].start();
        }
    }          
}

이렇게 수행하면 기부금을 받는 단체인 group 객체를 매번 새로 생성했기 때문에, 10명의 기부자가 10개의 각기 다른 단체에 기부하는 상황이 될 것이다.

synchronized method

  • 문제점
  • 구현
profile
매일 1퍼센트씩 나아지기 ୧(﹒︠ ̫ ̫̊ ̫﹒︡)୨

0개의 댓글