Daemon 이란 백그라운드로 실행되면서, 사용자의 인터페이스(tty)가 없는 프로그램을 말한다. 우리가 흔히 사용하는 리눅스 서비스들은 대부분 데몬으로 동작하며, -d로 끝나는 프로그램(예: sshd, syslogd)이 모두 해당된다. 리눅스 명령인 nohup으로 백그라운드 구동은 가능하지만, kill 명령으로 종료해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 |
package com.bagesoft.test.daemon; import org.apache.commons.daemon.Daemon; import org.apache.commons.daemon.DaemonContext; import org.apache.commons.daemon.DaemonInitException; public class TestDaemon implements Daemon, Runnable { private String status = "" ; private int no = 0 ; private Thread thread = null ; @Override public void init(DaemonContext context) throws DaemonInitException, Exception { System.out.println( "init..." ); String[] args = context.getArguments(); if (args != null ) { for (String arg : args) { System.out.println(arg); } } status = "INITED" ; this .thread = new Thread( this ); System.out.println( "init OK." ); System.out.println(); } @Override public void start() { System.out.println( "status: " + status); System.out.println( "start..." ); status = "STARTED" ; this .thread.start(); System.out.println( "start OK." ); System.out.println(); } @Override public void stop() throws Exception { System.out.println( "status: " + status); System.out.println( "stop..." ); status = "STOPED" ; this .thread.join( 10 ); System.out.println( "stop OK." ); System.out.println(); } @Override public void destroy() { System.out.println( "status: " + status); System.out.println( "destroy..." ); status = "DESTROIED" ; System.out.println( "destroy OK." ); System.out.println(); } @Override public void run() { while ( true ) { System.out.println(no); try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } if (no > 1000 ) { break ; } no++; } } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 |
JAVA_HOME= /opt/jdk1 .6.0_24 JSVC= /root/commons-daemon/jsvc USER=root DAEMON_HOME= /root/commons-daemon PID_FILE=$DAEMON_HOME /daemon .pid OUT_FILE=$DAEMON_HOME /daemon .out #ERR_FILE=$DAEMON_HOME/daemon.err CLASSPATH=\ $DAEMON_HOME /commons-daemon-1 .0.5.jar:\ $DAEMON_HOME /BageSoft .jar MAIN_CLASS=com.bagesoft. test .daemon.TestDaemon case "$1" in start) # # Start Daemon # rm -f $OUT_FILE $JSVC \ -user $USER \ -java-home $JAVA_HOME \ -pidfile $PID_FILE \ -outfile $OUT_FILE \ -errfile $OUT_FILE \ - cp $CLASSPATH \ $MAIN_CLASS # # To get a verbose JVM #-verbose \ # To get a debug of jsvc. #-debug \ exit $? ;; stop) # # Stop Daemon # $JSVC \ -stop \ -nodetach \ -java-home $JAVA_HOME \ -pidfile $PID_FILE \ -outfile $OUT_FILE \ -errfile $OUT_FILE \ - cp $CLASSPATH \ $MAIN_CLASS exit $? ;; *) echo "[Usage] TestDaemon.sh start | stop" exit 1;; esac |
최근 singleton 패턴 구현에 대한 질문을 받은 적이 있다. singleton 패턴은 인스턴스를 하나만 생성해서 그 객체를 공유해서 사용하는 패턴이다. 일반적으로 DB Pool과 같이 전체 시스템에서 하나의 자원을 공유해서 사용할 때 이 패턴을 활용할 수 있다.
이 패턴을 구현하는 방법은 synchronized getInstance(), double-checked getInstance(), static final instance 등 다양한데, 내 경우는 Singleton 클래스를 구현하기 위해 static initializer를 활용한 방법을 제안했다.
이렇게 구현한 경우 클래스가 로딩되면서 객체가 생성되기 때문에 객체가 이중으로 생성될 수 없고, 가장 확실한 방법인 synchronized getInstance()에 비해서 매번 메소드 호출시에 동기화 작업을 안해줘도 되는 장점이 있다. (synchronized 키워드가 있는 경우 메소드 실행이 약 100배 정도 늦어진다고 한다.)
하지만 오늘 관련 정보를 찾아보니, 글 작성자는 명백히 이 방법보다는 synchronized getInstance()가 좋다고 한다.
getInstance()에서 객체를 생성할 때 장점은 lazy initialization(객체가 사용될 때까지 객체 생성을 연기)이 가능하다는 점과 내부 구현을 Pooling 등으로 변경할 때 쉽게 변경이 가능하다는 것이다. (이 부분은 static initializer도 가능하다.)
다만 Singleton 패턴을 사용해도 ClassLoader 마다 개별적으로 인스턴스가 생성되기 때문에 이 부분은 주의를 기울여야 한다. 특히 Servlet Container는 여러개의 ClassLoader를 사용할 수 있으니 이 점을 인지하고 있어야 한다.