객체가 너무 많아지면 컴퓨터 자원을 과도하게 사용하게 되고, 이는 프로그램 전체의 속도를 느리게 할 수 있다.
→ 개발자는 객체의 최대 개수를 제한할 필요가 생긴다.
싱글턴 패턴 : 최대 N개로 객체 생성을 제한하는 패턴
→ 여기서 중요한 것은 생성되는 객체의 최대 개수를 제한하는 데 있어 객체의 생성을 요청하는 쪽에서는 일일이 신경쓰지 않아도 되도록 만들어주는 것이다.
일반 자바 프로그래밍
게임 프로그래밍
public class Database {
private static Database singleton;
private String name;
public Database(String name) {
super();
this.name = name;
}
public static Database getInstance(String name) {
if (singleton == null) {
singleton = new Database(name);
}
return singleton;
}
public String getName() {
return name;
}
}
Test code 사용 예시
public class TestPattern1 {
public static void main(String[] args) {
Database database;
database = Database.getInstance("첫 번째");
System.out.println("database.getName() = " + database.getName());
database = Database.getInstance("두 번째");
System.out.println("database.getName() = " + database.getName());
Database d1 = new Database("1");
Database d2 = new Database("2");
Database d3 = new Database("3");
Database d4 = new Database("4");
Database d5 = new Database("5");
Database d6 = new Database("6");
System.out.println("database use");
}
}
아래의 코드 중 생성자 메서드는 실제 DB 커넥션을 하는 것 처럼 효과를 주기 위함이다.
public class Database {
private static Database singleton;
private String name;
// public Database(String name) {
// super();
// this.name = name;
// }
private Database(String name) {
try {
Thread.sleep(100);
this.name = name;
} catch (Exception e) {
e.printStackTrace();
}
}
public static Database getInstance(String name) {
if (singleton == null) {
singleton = new Database(name);
}
return singleton;
}
public String getName() {
return name;
}
}
Test code 사용 예시
이 처럼 싱글턴은 스레드에 취약점을 가지고 있다.
public class TestPattern2 {
static int nNUm = 0;
public static void main(String[] args) {
Runnable task = () -> {
try {
nNUm++;
Database database = Database.getInstance(nNUm + "번째 Database");
System.out.println("This is the " + database.getName());
} catch (Exception e) {
e.getStackTrace();
}
};
for (int i = 0; i < 10; i++) {
Thread t = new Thread(task);
t.start();
}
}
}
Database 클래스 중 getInstance() 코드 예시
public class Database {
private static Database singleton;
private String name;
private Database(String name) {
try {
Thread.sleep(100);
this.name = name;
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized static Database getInstance(String name) {
if (singleton == null) {
singleton = new Database(name);
}
return singleton;
}
public String getName() {
return name;
}
}
Static 키워드의 특성을 잘 활용한다. - 생성자 생성 x 직접 생성 o
public class Database {
private static Database singleton = new Database("product");
private String name;
private Database(String name) {
try {
Thread.sleep(100);
this.name = name;
} catch (Exception e) {
e.printStackTrace();
}
}
public static Database getInstance(String name) {
return singleton;
}
public String getName() {
return name;
}
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.time.LocalDateTime;
public class LogWriter {
private static LogWriter singleton = new LogWriter();
private static BufferedWriter bw;
private LogWriter() {
try {
bw = new BufferedWriter(new FileWriter("log.txt"));
} catch (Exception e) {
e.getStackTrace();
}
}
public static LogWriter getInstance() {
return singleton;
}
public synchronized void log(String str) {
try {
bw.write(LocalDateTime.now() + " : " + str + "\n");
bw.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void finalize() {
try {
super.finalize();
bw.close();
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
Test code 사용 예시
public class TestPattern1 {
public static void main(String[] args) {
LogWriter loggger;
loggger = LogWriter.getInstance();
loggger.log("홍길동");
loggger = LogWriter.getInstance();
loggger.log("전우치");
}
}
// 웹 상의 많은 페이지를 동시에 열어 본다는 것은 쓰레드에서 동시에 메서드를 호출하는 것과 동일 하다.
public class TestPattern2 {
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
// 쓰레드 마다 구분되는 로그 작성용 파라미터
Thread thread = new ThreadSub(i);
thread.start();
}
}
}
// 외부 클래스
class ThreadSub extends Thread {
int num;
public ThreadSub(int num) {
this.num = num;
}
@Override
public void run() {
LogWriter logger = LogWriter.getInstance();
if (num < 10) {
logger.log("*** 0" + num + "***");
} else {
logger.log("*** " + num + "***");
}
}
}