싱글톤 패턴
두 개 이상의 인스턴스가 생성되는 것을 막고, 인스턴스가 사용될 때에는 동일 인스턴스를 사용하게 하는 패턴
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(); } } }