프로세스와 스레드
운영체제에서는 실행중인 하나의 애플리케이션을 프로세스(Process)라고 부른다. 사용자가 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는데 이것이 프로세스이다.
스레드는 사전적 의미로 한 가닥의 실이라는 뜻인데, 한 가지 작업을 실행하기 위해 순차적으로 실행할 코드를 실처럼 이어 놓았다고 해서 유래된 이름이다. 하나의 스레드는 하나의 코드 실행 흐름이다.
멀티 스레드는 스레드들이 동시에 실행되는 것처럼 보이는 기술이라고 할 수 있다. 예를 들어보면
A 작업어플 - 프로세스 100개 만들기
B 어플 - 프로세스1개 , 스레드 100개 만들기
A와 B 둘중에 어느 작업이 더 효울적일까?
답은 B의 작업이다.
▷ 멀티 태스킹
멀티 태스킹은 두 가지 이상의 작업을 동시에 처리하는 것을 말한다. 운영체제는 멀티 태스킹을 할 수 있도록 CPU 및 메모리자원을 프로세스마다 적절히 할당해주고, 병렬로 실행시킨다. 멀티 프로세스가 애플리케이션 단위의 멀티 태스킹이라면 멀티 스레드는 애플리케이션 내부의 멀티 태스킹이라고 볼 수 있다.
자바에서 제공되는 스레드는 main이며 그 안에서 main() 메서드가 호출이된다.새로운 스레드를 생성하고 시작을 하면 또 다른 스레드가 만들어진다. main스레드와 다른 한 개의 스레드가 CPU를 점유하려고 한다.
서로 점유하려고 싸우고 있으며 두 개 이상의 멀티 스레드가 CPU를 점유하려고 다투기 때문에 컴퓨터가 성능이 좋을 수 록 거의 동시에 처리하는 모습으로 나타난다.
▷ 메인 스레드
모든 자바 애플리케이션은 메인 스레드(main thread)가 main() 메서드를 실행하면서 시작된다.
메인스레드는 필요에 따라 작업 스레들을 만들어서 병렬로 코드를 실행할 수 있다. 즉 멀티 스레드를 생성해서 멀티 태스킹을 수행한다.
메인스레드가 종료되면 프로세스도 종료된다. 하지만 멀티 스레드 애플리케이션에서는 실행중인 스레드가 하나라도 있다면, 프로세스는 종료되지 않는다. 메인 스레드가 작업 스레드보다 먼저 종료되더라도 작업 스레드가 계속 실행중이라면 프로세스는 종료되지 않는다.
public class ThreadTest {
public static void m() {
Thread t= Thread.currentThread(); //현재사용중인 스레드를 얻을 수 있다.
System.out.println("m()를 호출한 thread:"+ t.getName());
}
public static void main(String[] args) {
Thread t= Thread.currentThread(); //현재사용중인 스레드를 얻을 수 있다.
System.out.println("main()를 호출한 thread:"+ t.getName());
m();
}
}
결과값
main()를 호출한 thread:main
m()를 호출한 thread:main
자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필요하다. java.lang.Thread 클래스를 직접 객체화해서 생성해도 되지만, Thread 를 상속해서 하위클래스를 만들어 생성할 수도 있다.
- Thread 클래스로부터 직접 생성
▷ Thread 클래스로부터 직접 생성
java.lang.Thread 클래스를 직접 객체화하려면 Runnable을 매개값으로 갖는 생성자를 호출해야 한다.
Thread thread = new Thread(Runnable target);
Runnable은 작업스레드가 실행할 수 있는 코드를 가지고 있는 객체라고 해서 붙여진 이름이다. Runnable은 인터페이스 타입이기 때문에 구현 객체를 만들어 대입해야 한다.
Runnable에는 run() 메서드가 정의되어 있다. 구현 클래스에서 run() 을 재정의해서 작업 스레드가 실행할 코드를 작성해야한다.
class Task implements Runnable {
public void run() {
스레드가 실행할 코드;
}
}
Runnable은 작업 내용을 가지고 있는 객체이지 실제 스레드가 아니다. Runnable구현객체를 생성한 후, 이것을 매개 값으로 해서 Thread 생성자를 호출하면 그때 작업 스레드가 생성된다.
Runnable task = new Task();
Thread thread = new Thread(task);
다른 방법으로는 익명구현 객체와 람다식 방법이 있다.
1. 익명구현 객체
Thread thread = new Thread(new Runnable() {
public void run() {
//스레드가 실행할 코드;
}
});
2. 람다식
Thread thread = new Thread(()-> {
//스레드가 실행할 코드;
});
작업스레드는 생성되는 즉시 실행되는 것이 아니라, start() 메서드를 호출해야만 실행된다.
thread.start();
start() 메서드가 호출되면, 작업 스레드는 매개값으로 받은 Runnable의 run() 메서드를 샐행하면서 자신의 작업을 처리한다.
▷ Thread 하위 클래스로부터 생성
작업 스레드가 실행할 작업을 Runnable 로 만들지 않고, Thread 의 하위클래스로 작업 스레드를 정의하면서 작업내용을 포함시킬 수 있다.
Thread 클래스를 상속한 후 run메서드를 재정의(overriding)해서 스레드가 실행할 코드를 작성하면 된다.
public class WorkerThread extends Thread {
@Override
public void run() {
//스레드가 실행할 코드
}
Thread thread = new WorkerThread();
같은 코드를 다음과 같이 Thread 익명객체로 작업 스레드 객체를 생성할 수 있다.
Thread thread = new Thread() {
public void run() {
//스레드가 실행할 코드;
}
}
Video, Sound, main 스레드가 cpu를 서로 점유하기 위해 다투게 된다. 할일이 끝나게 되면 스레드가 종료된다. 하지만 main 스레드 같은 주 스레드는 자기가 파생시킨 스레드를 기다렸다가 다 끝나면 종료가 된다.
class Video extends Thread {
@Override // 매서드 오버라이딩 //thread가 cpu를 점유해서 할일
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("비디오" + i);
}
}
}
public class ThreadTest {
public static void m() {
Thread t = Thread.currentThread(); // 현재사용중인 스레드를 얻을 수 있다.
System.out.println("m()를 호출한 thread:" + t.getName());
}
public static void main(String[] args) {
Thread t = Thread.currentThread(); // 현재사용중인 스레드를 얻을 수 있다.
System.out.println("main()를 호출한 thread:" + t.getName());
// Video //스레드를 생성하고 시작.
Video v = new Video();
//v.run(); //직접 run메서드를 시작하면 안된다. 이렇게 되면 main()스레드만 작동
v.start(); //thread라는 부모에서 제공함 start()매서드를
m();
}
}
mian스레드와 새로운 스레드와 cpu를 점유하려고 한다. 아래의 결과값에서는 m()메서드호출 결과가 먼저 나온 것을 볼 수 있다.
결과값
main()를 호출한 thread:main
m()를 호출한 thread:main
비디오1
.
.
.
비디오50
위의 코드에서 보다 더 많은 스레드를 만든다고 한다면 어떤 스레드가 점유하는지 알기가 쉽지 않다.
스레드의 run 메서드를 빠져나오는 방법
class Stop extends Thread{
boolean flag = true;
public void run() {
while(flag) { //무한반복시
System.out.println("run");
}
}
}
class Stop1 extends Thread{
int max = 10000;
public void run() {
for(int i =0; i<max; i++) {
System.out.println("run:"+i);
}
}
}
public class StopTest {
public static void main(String[] args) {
Stop s = new Stop();
s.start();
try {
Thread.sleep(1000);
s.flag = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Stop1 s1 = new Stop1();
s1.start();
s1.max = 0;
}
}
- while 문 사용시 flag로 조건문을 조절하여 스레드 중지
- for 문 사용시 max 값을 조절하여 스레드 중지
▷ 스레드의 이름
스레드는 자신의 이름을 가지고 있다. 직접 생성한 스레드는 자동적으로 "Thread-n"이라는 이름으로 설정된다. n은 스레드의 번호를 말한다. 다른 이름으로 설정하고 싶다면 Thread 클래스의 setName() 메서드로 변경할 수 있다.
thread.setName("스레드 이름");
반대로 스레드 이름을 알고 싶은 경우에는 getName() 메서드를 호출한다.
thread.getName();
setName()과 getName()은 Thread의 인스턴스 메서드이므로 스레드 객체의 참조가 필요하다. 만약 스레드 객체의 참조를 가지고 있지 않다면, Thread의 정적 메서드인 currentThread() 로 코드를 실행하는 현재 스레드의 참조를 얻을 수 있다.
Thread thread = Thread.currentThread();
'java' 카테고리의 다른 글
java_6_final (0) | 2021.11.02 |
---|---|
java_6_정적멤버와 static (0) | 2021.11.01 |
SOLID 객체지향설계 (0) | 2021.10.31 |
java_16객체입출력보조스트림 ( ObjectOutputStream & ObjectInputSteam) (0) | 2021.10.29 |
java_15_Io 패키지- 출력스트림(Writer) (0) | 2021.10.29 |