한개의 프로그램 (한개의 프로세스)에서 여러개의 동시(시분할)작업을 시키고 싶을때.
하나의 프로세스 안에서 여러개의 병렬 Thread가 동시에 진행될수 있다.
여러개의 프로세스를 동시작업을 하면 속도, 리소스의 비용이 많이 들기 때문에
한개의 프로세스안에서 여러 쓰레드(작업단위)를 두어,
가벼운 프로세스를 여러개 돌리는 효과
즉 작업의 단위가 여러개 라는건 반복문을 멀티로 돌릴 수 있다.
기본적으로 자바에서는 모든 스레드가 종료되어야 main이 끝난다... 왜 .. 알잖아? join에서 대기하고 있는거..(기본적으론 말야)
하지만 상황에 따라서는 백그라운드로 작업되어야 하는경우도 있다.(데몬화)
아무튼 그냥 스레드를 생성하고 띄우게 되면
--------------------------------------------------------------------------------------------------------------
package thread;
public class NormalThreadTest {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
try {
Thread.sleep(5000);
System.err.println("MyThread 종료");
} catch(Exception e) {
e.printStackTrace();
}
}
};
t.start();
// main thread 종료
System.out.println("main() 종료");
}
}
위의 결과는
main() 종료
MyThread 종료
이다. 즉 main 은 다른 스레드가 종료될때까지 대기하게 된다. 기본적으로 모든 스레드가 종료되어야 main이 끝나게 자바는 되어있다 그거지~
허나 setDaemon으로.. 데몬설정을 하면... ?
package thread;
import java.io.BufferedWriter;
import java.io.FileWriter;
public class DeamonThreadTest {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
try {
Thread.sleep(5000);
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\test123"));
bw.write("5초뒤에 나온 파일");
bw.close();
System.err.println("MyThread 종료");
} catch(Exception e) {
e.printStackTrace();
}
}
};
t.setDaemon(true);
t.start();
// main thread 종료
System.out.println("main() 종료");
}
}
main() 종료
스레드를 기다리지 않고 그냥 끝낸다.
그럼 daemon화 되었을때... 어떻게 기다리게할까?
-------------------------------------------------------------------------------------------------------------------
package thread;
import java.io.BufferedWriter;
import java.io.FileWriter;
public class DeamonThreadTest {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
try {
Thread.sleep(5000);
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\test123"));
bw.write("5초뒤에 나온 파일");
bw.close();
System.err.println("MyThread 종료");
} catch(Exception e) {
e.printStackTrace();
}
}
};
t.setDaemon(true);
t.start();
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// main thread 종료
System.out.println("main() 종료");
}
}
MyThread 종료
main() 종료
마지막의 main()가 종료되기전에 내부 thread가 종료되어서야 끝난다.
join은 대기하게 해주는 역할을 하게 되지... (너무 막말한다; )
2.6.3.4 Daemon Thread.
모든 Java thread는 daemon thread가 될 수 있다. Daemon thread는 같은 process안에서 다른 thread나 object를 service해주는 daemon thread이다. 예를 들어서, HotJava browser는 image를 필요로하는 thread나 object에게 filesystem이나 network으로부터 image를 읽어주는 기능을 하는 "Background Image Reader"라는 daemon thread를 가지고 있다.
Daemon thread는 전형적으로 같은 application에 있는 object를 위해서 service를 제공하는 독립적인 thread이다. Daemon thread를 위한 run() method는 전형적으로 service의 요청을 기다리며 무한 loop를 수행한다.
Process에 오직 daemon thread 혼자만 남아있을때, interpreter는 exit한다. 즉 이때는, service를 요청하는 thread가 더이상 존재하지 않고, daemon thread만이 존재할때를 말한다.
true를 parameter로 하여 setDaemon() method를 호출하면, thread는 daemon thread가 된다. isDaemon() method를 사용하여 thread가 daemon thread인지 알아볼 수 있다.
from
http://mind.kaist.ac.kr/~jhlee/javadoc_special/progGuide/WriteJava.html=================================================================================================================
스레드에도 그룹이 있다. 지원이 약해서잘 쓰이진 않는단다.
처음에는 main그룹이지.
ThreadGroup(String name)
name 이라는 그룹을 만든다. 최상위 가 된다...
ThraedGroup(ThreadGroup parent, String name)
parent 라는 그룹을 부모로 가지는 name이라는 그룹을 만든다.
public class ThreadGroupTest {
public static void main(String args[]) {
// main 스레드에 대한 정보를 출력
System.out.println("ThreadGroupTest: " + Thread.currentThread());
// main 스레드그룹의 서브 그룹을 만듬
ThreadGroup tGroup1 = new ThreadGroup(Thread.currentThread().getThreadGroup(), "ThreadGroup1");
// main 스레드그룹의 서브 그룹을 만듬
ThreadGroup tGroup2 = new ThreadGroup("ThreadGroup2");
// tGroup1 스레드그룹의 서브 그룹을 만듬
ThreadGroup tGroup3 = new ThreadGroup(tGroup1, "ThreadGroup3");
Thread t1 = new Thread(tGroup1, "Thread-1");
Thread t2 = new Thread(tGroup2, "Thread-2");
Thread t3 = new Thread(tGroup3, "Thread-3");
System.out.println(" t1: " + t1);
System.out.println(" t2: " + t2);
System.out.println(" t3: " + t3);
System.out.println(
"main 스레드그룹: "
+ Thread.currentThread().getThreadGroup()
+ ", 활동중인 스레드 개수:"
+ Thread.currentThread().getThreadGroup().activeCount()
+ ", 활동중인 스레드그룹 개수:"
+ Thread.currentThread().getThreadGroup().activeGroupCount());
// main 스레드그룹의 리스트를 출력.
Thread.currentThread().getThreadGroup().list();
}
}
---------- 자바 실행 ----------
ThreadGroupTest: Thread[main,5,main]
t1: Thread[Thread-1,5,ThreadGroup1]
t2: Thread[Thread-2,5,ThreadGroup2]
t3: Thread[Thread-3,5,ThreadGroup3]
main 스레드그룹: java.lang.ThreadGroup[name=main,maxpri=10], 활동중인 스레드 개수:1, 활동중인 스레드그룹 개수:3
java.lang.ThreadGroup[name=main,maxpri=10]
Thread[main,5,main]
java.lang.ThreadGroup[name=ThreadGroup1,maxpri=10]
java.lang.ThreadGroup[name=ThreadGroup3,maxpri=10]
java.lang.ThreadGroup[name=ThreadGroup2,maxpri=10]
출력 완료 (0초 경과) - 정상 종료
-----------------------------------------------------------------------
스레드의 우선순위........
지표로서의 의미가 크지.. 이것을 가지고 동기화에 사용되어서는 안된다!
등급레벨
1~10
10 = 최고등급
1 = 최소등급
5 = 기본등급
누군가가 말하기를
10 : 위기관리
7~9 : 상호작용 , 이벤트처리
4~6 : IO관련작업
2~3 : 백그라운드 작업
1 : 기타 다른 작업이 없을때 실행
class PriorityThread implements Runnable {
public void run() {
try {
// isInterrupted() 메소드를 while 문 조건으로 사용. 만약 이 스레드에 interrupt() 메소드를 호출하면
//isInterrupted() 메소드는 true 를 리턴해서 while 문을 빠져나가게 된다.
while (!Thread.currentThread().isInterrupted()) {
// PriorityThread 의 우선순위를 출력.
System.out.println("Priority : " + Thread.currentThread().getPriority());
// 0.5초간 멈춤.
Thread.sleep(500);
}
} catch (InterruptedException e) {
// 예상했던 예외이므로 무시..
}
}
}
public class PriorityThreadTest {
public static void main(String args[]) throws InterruptedException {
System.out.println("Start Main..");
System.out.println("Thread.MAX_PRIORITY : " + Thread.MAX_PRIORITY);
System.out.println("Thread.MIN_PRIORITY : " + Thread.MIN_PRIORITY);
System.out.println("Thread.NORM_PRIORITY : " + Thread.NORM_PRIORITY);
// 스레드를 생성하고 시작 시킴.
// 기본적으로 Thread.NORM_PRIORITY, 즉 5의 우선순위 값을 갖게됨.
Thread t = new Thread(new PriorityThread());
t.start();
Thread.sleep(500);
// 우선순위를 Thread.MIN_PRIORITY, 즉 1로 바꿈.
t.setPriority(Thread.MIN_PRIORITY);
Thread.sleep(500);
// 우선순위를 8로 바꿈.
t.setPriority(8);
Thread.sleep(500);
// 우선순위를 Thread.MAX_PRIORITY, 즉 10으로 바꿈.
t.setPriority(Thread.MAX_PRIORITY);
Thread.sleep(500);
// 스레드를 종료시킴.
t.interrupt();
System.out.println("End Main..");
}
}
---------- 자바 실행 ----------
Start Main..
Thread.MAX_PRIORITY : 10
Thread.MIN_PRIORITY : 1
Thread.NORM_PRIORITY : 5
Priority : 5
Priority : 5
Priority : 8
Priority : 10
Priority : 10
End Main..
출력 완료 (2초 경과) - 정상 종료
-----------------------------------------------------------------------------------------
동기화
기본적으로 동기화 문제를 해결하기 위해 모든 객체에 락이라는것을 포함시켰다. (자바기본) 눈에 보이지 않지만 +_+
이 락을 사용하는것이 바로 synchronized 이다.
알다시피 블럭모드와 메소드에 붙이는.. 2가지 형태가 존재한다.
락을 모니터라는것이 검사하는데, 락과 마찬가지로 모뇌터도 각 객체의 레퍼런스와 연결되어있다. 락과 함께 모니터도 자동생성이다.
시나리오는 어떤 스레드가 synchronized 를 사용한 메소드나 블럭에 접근하면 그와 관련된 모니터는 락을 검사하여
사용할 수 있는지 등의 여부를 알려준다.
Object최상위를 보면 알겠지만 wait(), notify()가 존재함을 알 수 있다.
그리고 이 메소드들을 호출할려면 반드시 해당 객체의 락을 얻어야 한다는것도...
즉.. synch 되어있는 상태에서 사용해야한다는 거지~ 그냥 wait, notify.. 하면 에러나온다구~
제6장 스레드
1. 프로세스와 스레드의 생성 및 종료
가. 프로세스 생성 및 종료
일반적으로 프로그램을 실행시키면, 하나의 프로세스로서 동작하게 됩니다. 다시 말해서, 우리가 실행시키는 하나의 프로그램은 하나의 프로세스로서 나타나게 됩니다. 자바에서의 프로세스는 자바 런타임 환경과 밀접한 관계를 갖고 있습니다. 왜냐하면, 자바 런타임 환경은 프로세스가 실행될 수 있는 기반 환경을 제공해 주기 때문입니다. 프로세스는 다른 프로세스를 생성할 수 있는데, 이 때 생성된 프로세스를 자식 프로세스라하고 기존에 있던 프로세스를 부모 프로세스라 합니다. 이러한 부모/자식 프로세스 개념은 하나의 자바 프로그램에서 다른 프로그램을 실행시키고자 할 때, 주로 사용됩니다. 다시 말해서, 플랫폼 독립적인 자바 프로그램이 플랫폼과 밀접한 관련이 있는 작업을 해야 할 경우, 해당 작업을 수행할 프로그램을 다른 언어로 해당 플랫폼에 맞도록 작성하고, 이 프로그램을 자바 프로그램에서 실행시켜 주는 것입니다.
이를 위해, 플랫폼 종속적인 시스템 함수들을 호출할 수 있도록 해 주는 Runtime 클래스와 실행하고자 하는 응용프로그램을 위한 프로세스를 관리할 수 있도록 해 주는 Process 클래스를 사용할 수 있습니다. 자바에서 프로세스를 생성하기 위하여 다음과 같이 해 줍니다.
- “Runtime runtime = Runtime.getRuntime();”: 런타임 객체를 생성합니다.
- “Process p = runtime.exec(“프로그램경로명”);”: exec 메소드를 이용하여 프로세스를 생성합니다.
위와 같이 프로세스를 생성할 수 있고, 프로세스의 작업을 마치거나 또는 프로세스를 강제고 종료하기 위해서는 다음 중 한 가지 방법으로 할 수 있습니다.
- “p.waitFor();”: 자식 프로세스가 종료될 때까지 기다립니다.
- “p.destroy();”: 부모 프로세스에서 자식 프로세스를 강제로 종료시킵니다.
- “System.exit(0);”: 부모 프로세스만 종료되고 자식 프로세스는 계속 실행됩니다.
Runtime 클래스가 제공해 주는 주요 메소드를 살펴보면 다음과 같습니다.
- public static Runtime getRuntime(): 현재 실행되고 있는 자바 애플리케이션과 관련된 런타임 객체를 리턴해 줍니다.
- public void exit(int status): 현재 자바 가상머신을 종료합니다. status 매개변수는 종료시의 상태값을 나타내며, 일반적으로 0 이외의 값은 비정상적으로 종료되었음을 의미합니다.
- public Process exec(String command) throws IOException: 주어진 명령어를 독립된 프로세스로 실행시켜 줍니다. exec(command, null)와 같이 실행시킨 것과 같습니다.
- public Process exec(String command, String envp[]) throws IOException: 주어진 명령어를 주어진 환경을 갖는 독립된 프로세스로 실행시켜 줍니다. 이 메소드는 명령어 문자열을 토큰으로 나누어 이 토큰들을 포함하고 있는 cmdarray라는 새로운 배열을 생성합니다. 그리고 나서 exec(cmdarray, envp)을 호출합니다.
- public Process exec(String cmdarray[]) throws IOException: 주어진 문자열 배열에 있는 명령어와 매개변수를 이용하여 독립된 프로세스로 실행시켜 줍니다. exec(cmdarray, null)을 호출합니다.
- public Process exec(String cmdarray[], String envp[]) throws IOException: 주어진 문자열 배열에 있는 명령어와 매개변수를 이용하여 주어진 환경을 갖는 독립된 프로세스로 실행시켜 줍니다. 문자열 배열 cmdarray에는 명령어와 명령행 인자들을 나타내고 있습니다.
- public native long freeMemory(): 시스템에 남아있는 메모리의 양을 얻습니다. 이 값은 항상 totalMemory() 메소드에 의해 얻어지는 값보다 작습니다.
- public native long totalMemory(): 자바 가상머신의 최대 메모리 크기를 얻습니다.
Process 클래스가 제공해 주는 주요 메소드를 살펴보면 다음과 같습니다.
- public abstract OutputStream getOutputStream(): 자식 프로세스의 출력 스트림을 얻습니다.
- public abstract InputStream getInputStream(): 자식 프로세스의 입력 스트림을 얻습니다.
- public abstract InputStream getErrorStream(): 자식 프로세스의 에러 스트림을 얻습니다.
- public abstract int waitFor() throws InterruptedException: 자식 프로세스가 종료될 때까지 기다립니다.
- public abstract int exitValue(): 자식 프로세스가 종료할 때의 상태값을 얻습니다.
- public abstract void destroy(): 자식 프로세스를 강제로 종료시킵니다.
다음에 나오는 자바 프로그램은 위의 Runtime 클래스 및 Process 클래스를 이용하여 새로운 프로세스를 생성하고 종료하는 과정을 보여주기 위해 윈도우의 계산기를 실행시키는 간단한 예제입니다.