디자인 패턴 – 싱글톤 패턴

싱글톤 패턴
두 개 이상의 인스턴스가 생성되는 것을 막고, 인스턴스가 사용될 때에는 동일 인스턴스를 사용하게 하는 패턴

Example)
하나의 프린터만 만들어서 사용해야 되는 상황

먼저 생성자를 Private로 만들어서 객체의 생성을 제한한다.

-> 객체 생성은 클래스의 멤버 함수(getPrinter)에서 진행한다. 즉 클래스가 Printer객체를 소유하는 개념이다.
getPrinter는 static 메소드를 이용한다. 따라서 Printer 클래스를 인스턴스화하지 않아도 메소드에 접근할 수 있게 한다.

[문제점]
멀티 스레드 환경에서 문제가 발생한다.
두 스레드가 getPrinter 메소드를 동시에 호출하면 if (printer == null) 조건이 여러 스레드에 모두 만족돼서 Printer 객체를 여러 스레드에서 생성할 수 있다.

[해결 방법]
1. 정적 변수에 인스턴스를 만들어 바로 초기화한다.


2. 메소드 동기화(synchronize)를 진행한다. 메소드를 제작할 때 synchronized 키워드를 이용하면 lock이 걸리게 된다. 즉 스레드 1번이 해당 메소드를 이용하면, 작업이 끝난 다음에야 스레드 2번이 사용할 수 있다.

[다른 문제점]
Printer객체에 counter 변수가 존재하면 일정하지 않게 업데이트될 수 있다.
만약 여러 스레드가 counter 변수의 값을 동시에 올리려고 시도한다고 하자.
스레드1이 counter+1까지 했는데, 스레드2가 counter=counter+1까지 진행하였다. 그러면 스레드1에서 counter값을 업데이트해도 값은 변경되지 않는다. 즉 두 스레드에서 counter++을 했는데 counter값은 1만 증가하게 된다.

[해결 방법]
syncronized 처리를 block 단위로 진행한다.


코드

class Printer{
	private static Printer printer=null;
	private int counter=0;
	private Printer() {}
	
	public synchronized static Printer getPrinter() {
		if(printer==null) {
			printer=new Printer();
		}
		return printer;
	}
	
	public void print() {
		synchronized(this) {
			counter++;
			System.out.print("출력 완료: "+counter+"회 ");
			System.out.println("[프린터 코드:"+this.toString()+"]");
		}
	}
}

class UserThread extends Thread{
	public UserThread(String name) {
		super(name);
	}
	
	public void run() {
		Printer p=Printer.getPrinter();
		p.print();
	}
}

public class test{
	public static void main(String[] args) {
		UserThread[] user=new UserThread[5];
		
		for(int i=0; i<5; i++) {
			user[i]=new UserThread(i+"");
			user[i].start();
		}
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *