프로세스(process)
프로세스(공장), 쓰레드(일꾼) : 싱글 쓰레드
프로세스(공장), 쓰레드들(일꾼들) : 멀티 쓰레드
쓰레드를 가벼운 프로세스, 즉 경량 프로세스(LWP, light-weight process)라고 부르기도 한다.
일반적으로 Runnable 인터페이스를 구현하는 방식을 사용함
public class ThreadEx1 {
public static void main(String[] args) {
ThreadEx1_1 t1 = new ThreadEx1_1();
Runnable r = new ThreadEx1_2();
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class ThreadEx1_1 extends Thread {
public void run() {
for(int i = 0; i < 5; i++) {
// Thread 클래스 메소드 직접 호출 가능
System.out.println(getName());
}
}
}
class ThreadEx1_2 implements Runnable {
@Override
public void run() {
for(int i = 0; i < 5; i++) {
// Runnable을 구현하면 Thread클래스의 currentThread()를 호출하여
// 쓰레드에 대한 참조를 얻어와서 호출 해야 함
System.out.println(Thread.currentThread().getName());
}
}
}
쓰레드의 실행
t1.start() // 쓰레드 t1을 실행시킨다.
t2.start() // 쓰레드 t2를 실행시킨다.
실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료 된다.
-쓰레드의 종류: 사용자 쓰레드(user thread), 데몬 쓰레드(deamon thread)
싱글 쓰레드와 멀티 쓰레드의 차이(싱글 코어)
| A 작업 | B 작업 |
|A|B|A|B|A|B|A|B|A|B|A|B|
싱글코어에서 단순히 CPU만을 사용하는 계산 작업이라면 오히려 멀티쓰레드보다 싱글쓰레드로 프로그래밍 하는 것이 더 효율적이다.
병행과 병렬
- 싱글코어로 두개의 쓰레드를 실행 및 처리하는 경우: 병행(concurrent)
- 멀티 코어로 두개의 쓰레드를 실행 및 처리하는 경우: 병렬(parallel)
두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우에는 싱글쓰레드 프로세스보다 멀티 쓰레드 프로세스가 더 효율적이다.- 예를 들면, 사용자로부터 데이터를 입력받는 작업, 네트워크로 파일을 주고받는 작업, 프린터로 파일을 출력하는 작업과 같이 외부기기와의 입출력이 필요로 하는 경우가 이에 해당된다.
쓰레드는 우선순위(priority)에 따라서 실행 시간이 달라진다.
void setPriority(int newPriority) // Thread 우선순위를 지정한 값으로 변경한다.
int getPriority() // Thread의 우선순위를 반환한다.
public static final int MAX_PRIORITY = 10;
public static final int MIN_PRIORITY = 1;
public static final int NORM_PRIORITY = 5; // 보통 우선 순위
쓰레드 그룹은 보안상의 이유로 도입된 개념으로, 자신이 속한 쓰레드 그룹이나 하위 쓰레드 그룹은 변경할 수 있지만 다른 쓰레드 그룹의 쓰레드를 변경할 수 없다.
Thread(ThreadGroup group, String name);
Thread(ThreadGroup group, Runnable target);
Thread(ThreadGroup group, Runnable target, String name);
Thread(ThreadGroup group, Runnable target ,String name, long stackSize);
java application을 실행하면 JVM은 main과 system이라는 쓰레드 그룹을 만든다.
데몬 쓰레드: 다른 일반 쓰레드(데몬 쓰레드가 아닌 쓰레드)의 작업을 돕는 보조적인 역할을 수행하는 쓰레드이다.
데몬쓰레드는 무한 루프와 조건문을 이용해서 실행 후 대기하고 있다가 특정 조건이 만족되면 직업이 수행되고 다시 대기하도록 작성한다.
boolean isDaemon() // 쓰레드가 데몬 쓰레드인지 확인한다.
void setDaemon(boolean on) // 쓰레드를 데몬 쓰레드로 또는 사용자 쓰레드로 변경한다.
// Daemon thread example
public class ThreadEx10 implements Runnable {
static boolean autoSave = false;
public static void main(String[] args) {
Thread t = new Thread(new ThreadEx10());
t.setDaemon(true); // **이 부분이 없으면 종료되지 않는다.**
t.start();
for(int i = 1; i <= 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
if(i==5) {
autoSave = true;
}
}
System.out.println("프로그램을 종료합니다.");
}
@Override
public void run() {
// **데몬쓰레드 실행을 위한 무한루프 실행 및 특정 조건 일때 실행**
while(true) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(autoSave) {
autoSave();
}
}
}
public void autoSave() {
System.out.println("작업파일이 자동저장되었습니다.");
}
}
Thread의 상태는 아래와 같다.
class ThreadEx15{
public static void main(String args[]) {
A th1 = new A();
B th2 = new B();
th1.start();
th2.start();
try {
th1.sleep(5000);
// sleep(): 작업 흐름 대기시간 설정한다.
// 5초동안 대기시간 갖은 후에 다음 문자의 실행흐름을 이어 나간다.
} catch(InterruptedException e) {}
System.out.print("<<main 종료>>");
} // main
}
class A extends Thread {
public void run() {
for(int i=0; i < 300; i++) {
System.out.print("-");
}
System.out.print("<<th1 종료>>");
} // run()
}
class B extends Thread {
public void run() {
for(int i=0; i < 300; i++) {
System.out.print("|");
}
System.out.print("<<th2 종료>>");
} // run()
}
interrupt()와 interrupted()
진행중인 쓰레드의 작업이 끝나기 전에 취소시켜야 할 때가 있는 경우 사용
Suspend(), resume(), sotp()
쓰레드의 실행을 제어하는 가장 손쉬운 방법이지만, suspend()와 stop()이 교착상태(deadlock)을 일으키기 쉽게 작성되어 있으므로 사용이 권장 되지는 않는다. (deprecated)
yield()
yield()와 interrupt()를 적절히 사용하면 프로그램의 응답성을 높이고 보다 효율적인 실행이 가능하게 할 수 있다.
join()