- 자바 프로그래밍
- 스레드 프로그래밍(com.eomcs.concurrent.ex1 ~ ex4)
- 멀티태스킹의 메커니즘 이해
- 프로세스 스케쥴링: Round Robin 방식, Priority + Aging 방식
- 컨텍스트 스위칭 개념
- 프로세스 복제(fork)방식과 스레드 방식 비교
- 스레드의 구동원리와 사용법
- 스레드의 라이프사이클 이해
- Thread 클래스와 Runnable 인터페이스 사용법
- 프로젝트 실습
리눅스 서버 접속 및 C 파일 테스트 해보기
# 리눅스 운영체제 설치
- 가상 머신 구축 시스템 설치: Virtualbox
- CLI 기반 가상 머신 관리 도구 설치: Vagrant
- 가상 머신 생성 및 설정
## 도구 준비
### Virtualbox 설치
- [Virtualbox 사이트](https://www.virtualbox.org/) 접속
- Downloads/호스트 OS에 맞는 패키지 다운로드
- Virtualbox 설치
### Vagrant 설치
- [Vagrant 사이트](https://www.vagrantup.com/) 접속
- Download 클릭
- 호스트 OS에 맞춰서 설치
- 예) macOS: `brew install vagrant`
- 예) Windows: 다운로드 후 설치
- 설치 확인
```bash
$ vagrant -v
```
## VirtualBox 가상 머신 관리
### 가상 머신 프로젝트 폴더 생성
```bash
~$ mkdir vagrant
~$ cd vagrant
~/vagrant$ mkdir ubuntu
~/vagrant$ cd ubuntu
~/vagrant/ubuntu$
```
### Variant Box(가상머신 이미지) 찾기
- [가상머신이미지 저장소 사이트](https://app.vagrantup.com/) 접속
- 'ubuntu' 검색
- 'ubuntu'의 'virtualbox' 링크 선택
### Vagrant 설정 파일(Vagrantfile) 준비
```bash
~/vagrant/ubuntu$ vagrant init ubuntu/trusty64
~/vm/centos$ cat Vagrantfile
Vagrant.configure("2") do |config|
# ...
end
```
### Box 다운로드 및 VM 생성, 실행
```bash
~/vagrant/ubuntu$ vagrant up
```
### 가상 머신에 ssh 접속
```bash
~/vagrant/ubuntu$ vagrant ssh
```
### VM 정지
```bash
~/vagrant/ubuntu$ vagrant halt
```
### VM 삭제
```bash
~/vagrant/ubuntu$ vagrant destroy
```
## Vagrantfile
### 클라우드에서 가져올 box 이름 지정하기
```
# https://vagrantcloud.com/search 사이트에서 box를 검색 할 수 있다.
config.vm.box = "box 이름"
예) config.vm.box = "ubuntu/trusty64"
```
## Box 다루기
### Vagrant에 설치된 박스 목록 보기
```bash
$ vagrant box list
```
### Vagrant에 등록된 박스 제거하기
```bash
$ vagrant box remove "박스이름"
예)
$ vagrant box remove "myUbuntu/trusty64"
```
네이버 클라우드 서버 접속 확인 및 update, upgrade 실행
ㄴ 네이버 클라우드 서버 접속해보기
ㄴ ssh root@공인 IP 주소
ㄴ apt update, apt upgrade 해주기
Vagrant 설치
ㄴ [Download] 선택
ㄴ 해당 코드 복사하여 실행
ㄴ 윈도우는 직접 다운로드
ㄴ Mac m1 에서 해당 코드 이용하여 다운로드
ㄴ 다운 받은 vagrant 의 버전 확인 가능
ㄴ 해당 폴더 생성
https://app.vagrantup.com/ubuntu/boxes/trusty64
ㄴ 해당 코드 이용
=>
=>
=>
ㄴ Mac 에서는 오류 발생
ㄴ Windows 에서는 VirtualBox 에 있는 서버 실행시킬 수 있음
C 파일 테스트
ㄴ nano 를 이용하여 코드 입력 후 command + O 로 a.c 라는 이름으로 저장해주고 command + x 로 나가기
ㄴ a.c 파일 컴파일 하기
ㄴ ./a 로 a.c 파일 실행
=> 현재 폴더에서 컴파일된 a 를 실행
ㄴ 실행 결과
ㄴ a.c 파일 변경
ㄴ 다시 컴파일 후 실행한 결과
ㄴ 새로운 파일 b.c 생성
ㄴ a.c 파일과 마찬가지로 b.c 라는 이름으로 저장하여 컴파일 한 후 실행한 결과
스레드
## 컨텍스트 스위칭(context switching)
- CPU의 실행 시간을 쪼개 이 코드 저 코드를 실행할 때 마다
실행 위치 및 정보(context)를 저장하고 로딩하는 과정이 필요하다.
- 이 과정을 '컨텍스트 스위칭'이라 부른다.
## 스레드(thread)
- '실'이라는 뜻을 갖고 있다.
- 한 실행 흐름을 가리킨다.
- 하나의 실은 끊기지 않은 하나의 실행 흐름을 의미한다.
## 스레드 생성
- 새 실을 만든다는 것이다.
- 즉 새 실행 흐름을 시작하겠다는 의미다.
- CPU는 스레드를 프로세스와 마찬가지로 동일한 자격을 부여하여 스케줄링에 참여시킨다.
- 즉 프로세스에 종속된 스레드라고 취급하여
한 프로세스에 부여된 실행 시간을 다시 쪼개 스레드에 나눠주는 방식이 아니다.
- 그냥 단독적인 프로세스처럼 동일한 실행 시간을 부여한다.
멀티 스레드 적용 전 - 멀티 태스킹을 적용하기 전
// 멀티 스레드 적용 전 - 멀티 태스킹을 적용하기 전
package com.eomcs.concurrent.ex1;
public class Exam0110 {
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
System.out.println("==> " + i);
}
for (int i = 0; i < 1000; i++) {
System.out.println(">>> " + i);
}
}
}
ㄴ 일반적으로 코드는 위에서 아래로 순서대로 실행함
ㄴ 작업이 완료할 때까지 다음 줄로 가지 않음
ㄴ 자바는 main() 메서드를 실행하는 한 개의 "실행 흐름"이 있음
ㄴ 실행 흐름에 따라 순서대로 코드가 실행됨
멀티 스레드 적용 후
// 멀티 스레드 적용 후
package com.eomcs.concurrent.ex1;
public class Exam0120 {
// CPU의 시간을 쪼개서 왔다갔다 하면서 동시에 실행하고픈 코드가 있다면,
// 다음과 같이 Thread를 상속 받아 run() 메서드에 그 코드를 두어라!
//
static class MyThread extends Thread {
@Override
public void run() {
// 기존 실행 흐름과 분리하여 따로 실행시킬 코드를
// 이 메서드에 둔다.
for (int i = 0; i < 1000; i++) {
System.out.println("==> " + i);
}
}
}
public static void main(String[] args) {
// => 동시에 실행할 코드를 담고 있는 Thread 객체를 생성한다.
// => 그리고 현재 실행과 분리하여 작업을 시작시킨다.
// => JVM은 이 스레드에 들어 있는 코드와 다음에 진행하는 코드를
// 왔다갔다 하면서 처리할 것이다.
new MyThread().start();
for (int i = 0; i < 1000; i++) {
System.out.println(">>> " + i);
}
}
}
ㄴ main() 메서드를 실행하는 기본 실행 흐름에서 새로운 실행 흐름으로 분기하고 싶다면, Thread 클래스를 정의할 때 분기해서 실행할 코드를 담으면 됨
=> 그러면 두 개의 실행 흐름이 서로 왔다 갔다 하면서 실행됨
현재의 실행 라인을 알아내기
// 현재의 실행 라인을 알아내기
package com.eomcs.concurrent.ex2;
public class Exam0110 {
public static void main(String[] args) {
// JVM은 여러 개의 스레드를 실행한다.
// main() 호출도 별도의 스레드가 실행한다.
// 확인해보자!
// 이 순간 실행 중인 흐름이 무엇인지 알고 싶다면?
Thread t = Thread.currentThread();
System.out.println("실행 흐름명 = " + t.getName());
// JVM이 실행될 때 main() 메서드를 호출하는 실행 흐름(스레드)의 이름은 "main"이다.
}
}
// JVM의 스레드 계층도:
// main(T)
=>
스레드 그룹
// 스레드 그룹
package com.eomcs.concurrent.ex2;
public class Exam0120 {
public static void main(String[] args) {
Thread main = Thread.currentThread();
// 스레드는 그룹에 소속되기도 한다.
// 현재 스레드의 소속 그룹을 알고 싶다면?
ThreadGroup group = main.getThreadGroup();
System.out.println("그룹명 = " + group.getName());
// main() 메서드를 호출하는 스레드는 "main" 스레드이고,
// "main" 스레드가 소속된 그룹은 "main" 그룹이다.
}
}
// JVM의 스레드 계층도:
// main(TG)
// => main(T)
=>
스레드 그룹에 소속된 스레드들
// 스레드 그룹에 소속된 스레드들
package com.eomcs.concurrent.ex2;
public class Exam0130 {
public static void main(String[] args) {
Thread main = Thread.currentThread();
ThreadGroup mainGroup = main.getThreadGroup();
// 스레드 그룹에 소속된 스레드 목록을 알고 싶다면?
Thread[] arr = new Thread[100];
int count = mainGroup.enumerate(arr, false);
// 두 번째 파라미터 값을 false로 지정하면,
// 하위 그룹에 소속된 스레드들은 제외한다.
// 즉, 현재 그룹에 소속된 스레드 목록만 가져오라는 뜻!
System.out.println("main 그룹에 소속된 스레드들:");
for (int i = 0; i < count; i++)
System.out.println(" => " + arr[i].getName());
}
}
// JVM의 스레드 계층도:
// main(TG)
// => main(T)
// => 다른 스레드는 없다.
=>
스레드 그룹에 소속된 하위 그룹들
// 스레드 그룹에 소속된 하위 그룹들
package com.eomcs.concurrent.ex2;
public class Exam0140 {
public static void main(String[] args) {
Thread main = Thread.currentThread();
ThreadGroup mainGroup = main.getThreadGroup();
// 스레드 그룹에 소속된 하위 그룹을 알고 싶다면?
ThreadGroup[] groups = new ThreadGroup[100];
int count = mainGroup.enumerate(groups, false);
// 두 번째 파라미터 값을 false로 지정하면,
// 하위 그룹에 소속된 그룹들은 제외한다.
// 즉, 현재 그룹에 소속된 하위 그룹의 목록만 가져오라는 뜻!
System.out.println("main 그룹에 소속된 하위 그룹들:");
for (int i = 0; i < count; i++)
System.out.println(" => " + groups[i].getName());
}
}
// JVM의 스레드 계층도:
// main(TG)
// => main(T)
// => 다른 하위 그룹은 없다!
=>
ㄴ main 그룹에 소속된 다른 하위 그룹은 없으므로 아무것도 출력되지 않음
스레드 그룹의 부모 그룹
// 스레드 그룹의 부모 그룹
package com.eomcs.concurrent.ex2;
public class Exam0150 {
public static void main(String[] args) {
Thread main = Thread.currentThread();
ThreadGroup mainGroup = main.getThreadGroup();
// 스레드 그룹의 부모 그룹을 알고 싶다면?
ThreadGroup parentGroup = mainGroup.getParent();
System.out.printf("main 스레드 그룹의 부모: %s\n", parentGroup.getName());
// "system" 그룹의 부모 그룹은?
ThreadGroup grandparentGroup = parentGroup.getParent();
if (grandparentGroup != null) {
System.out.printf("%s 스레드 그룹의 부모: %s\n",
parentGroup.getName(),
grandparentGroup.getName());
}
}
}
// JVM의 스레드 계층도:
// system(TG)
// => main(TG)
// ...=> main(T)
=>
ㄴ "system" 그룹의 부모 그룹은 존재하지 않으므로 출력되지 않음
"system" 스레드 그룹의 자식 그룹들
// "system" 스레드 그룹의 자식 그룹들
package com.eomcs.concurrent.ex2;
public class Exam0160 {
public static void main(String[] args) {
Thread main = Thread.currentThread();
ThreadGroup mainGroup = main.getThreadGroup();
ThreadGroup systemGroup = mainGroup.getParent();
ThreadGroup[] groups = new ThreadGroup[100];
int count = systemGroup.enumerate(groups, false);
System.out.println("system 스레드 그룹의 자식 그룹들:");
for (int i = 0; i < count; i++) {
System.out.println(" =>" + groups[i].getName());
}
}
}
// JVM의 스레드 계층도:
// system(TG)
// => main(TG)
// ...=> main(T) : main() 메서드를 호출한다.
// => InnocuousThreadGroup(TG)
=>
ㄴ system 스레드 그룹의 자식 그룹 => main, InnocuousThreadGroup 이 존재
ㄴ main() 메서드는 main 스레드가 호출함
스레드 만들기 I - Thread를 상속 받기
// 스레드 만들기 I - Thread를 상속 받기
package com.eomcs.concurrent.ex3;
public class Exam0110 {
public static void main(String[] args) {
// 1) Thread 클래스를 상속 받아 정의하기
class MyThread extends Thread {
// 기존의 스레드에서 분리해서 새 스레드에서 실행하고픈 코드가 있다면,
// run()을 재정의하여 그 메서드에 해당 코드를 두어라!
@Override
public void run() {
// 별도로 분리해서 병행으로 실행할 코드를 두는 곳!
for (int i = 0; i < 1000; i++) {
System.out.println("===> " + i);
}
}
}
// 스레드 실행
// => Thread의 서브 클래스는 그냥 인스턴스를 만들어 start()를 호출한다.
MyThread t = new MyThread();
t.start(); // 실행 흐름을 분리한 후 즉시 리턴한다. 비동기로 동작한다.
// "main" 스레드는 MyThread와 상관없이 병행하여 실행한다.
for (int i = 0; i < 1000; i++) {
System.out.println(">>>> " + i);
}
}
}
=>
ㄴ 똑같은 자바의 스레드 코드라도 OS에 따라 실행 순서가 달라질 수 있음
ㄴ Windows OS의 경우 우선 순위(priority) 값이 실행 순서나 실행 회수에 큰 영향을 끼치지 않음
Thread를 상속 받기 - 익명 클래스로 구현하기
// Thread를 상속 받기 - 익명 클래스로 구현하기
package com.eomcs.concurrent.ex3;
public class Exam0120 {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("===> " + i);
}
}
}.start();
for (int i = 0; i < 1000; i++) {
System.out.println(">>>> " + i);
}
}
}
ㄴ 익명클래스로 구현하기
스레드 만들기 II - Runnable 인터페이스 구현 + Thread
// 스레드 만들기 II - Runnable 인터페이스 구현 + Thread
package com.eomcs.concurrent.ex3;
public class Exam0210 {
public static void main(String[] args) {
// 2) Runnable 인터페이스를 구현하기
class MyRunnable implements Runnable {
@Override
public void run() {
// 별도로 분리해서 병행으로 실행할 코드를 두는 곳!
for (int i = 0; i < 1000; i++) {
System.out.println("===> " + i);
}
}
}
// 스레드 실행하기
// => Runnable 구현체를 Thread 객체에 실어서 실행한다.
// => start()를 호출하여 기존 스레드에서 분리하여 스레드를 실행시킨다.
Thread t = new Thread(new MyRunnable());
t.start(); // 실행 흐름을 분리한 후 즉시 리턴한다.
// "main" 스레드는 Thread와 상관없이 병행하여 실행한다.
for (int i = 0; i < 1000; i++) {
System.out.println(">>>> " + i);
}
}
}
ㄴ Runnable 인터페이스 구현
=> 인터페이스를 구현하는 것이기 때문에 다른 클래스를 상속 받을 수 있음
ㄴ 직접적으로 스레드가 아니기 때문에 실행할 때는 Thread의 도움을 받아야 함
unnable 인터페이스 구현 + Thread - 익명 클래스로 구현하기
// Runnable 인터페이스 구현 + Thread - 익명 클래스로 구현하기
package com.eomcs.concurrent.ex3;
public class Exam0220 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("===> " + i);
}
}
}).start();
for (int i = 0; i < 1000; i++) {
System.out.println(">>>> " + i);
}
}
}
ㄴ 익명 클래스로 구현하기
=>
Runnable 인터페이스 구현 + Thread - 람다(lambda)로 구현하기
// Runnable 인터페이스 구현 + Thread - 람다(lambda)로 구현하기
package com.eomcs.concurrent.ex3;
public class Exam0230 {
public static void main(String[] args) {
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println("===> " + i);
}
}).start();
for (int i = 0; i < 1000; i++) {
System.out.println(">>>> " + i);
}
}
static void m(Runnable obj) {
}
}
ㄴ 람다로 구현하기
스레드와 프로그램 종료
// 스레드와 프로그램 종료
package com.eomcs.concurrent.ex3;
import java.util.Scanner;
public class Exam0310 {
static class MyThread extends Thread {
@Override
public void run() {
Scanner keyboard = new Scanner(System.in);
System.out.print("입력하시오> ");
String input = keyboard.nextLine();
System.out.println("입력한 문자열 => " + input);
keyboard.close();
}
}
public static void main(String[] args) {
// main 스레드에서 새 스레드 객체 생성하기
// => 어떤 스레드에서 만든 스레드를 그 스레드의 자식 스레드라 부른다.
// => 즉 다음 스레드는 main 스레드의 자식 스레드이다.
// => 자식 스레드는 부모 스레드와 같은 우선 순위를 갖는다.
MyThread t = new MyThread(); // 우선순위 5
t.start();
// 모든 스레드가 완료할 때까지 JVM은 종료되지 않는다.
System.out.println("프로그램 종료?");
}
}
=>
ㄴ 모든 스레드가 완료할 때까지 JVM은 종료되지 않음
스레드의 생명주기(lifecycle)
// 스레드의 생명주기(lifecycle)
package com.eomcs.concurrent.ex4;
public class Exam0110 {
public static void main(String[] args) {
System.out.println("스레드 실행 전");
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("===> " + i);
}
}
}.start();
System.out.println("스레드 실행 후");
// main() 메서드의 호출이 끝나더라도 다른 스레드의 실행이 종료될 때까지
// JVM은 종료하지 않는다.
}
}
=>
스레드의 생명주기
new Thread() start() sleep()/wait()
준비 -------------------> Running ---------------> Not Runnable
^ | <---------------
| | timeout/notify()
X |
| | run() 메서드 종료
| V
Dead
Running 상태?
- CPU를 받아서 실행 중이거나 CPU를 받을 수 있는 상태
Not Runnable 상태?
- CPU를 받지 않는 상태
run() 메서드 종료 후 다시 running 상태로 돌아갈 수 없다.
=> 새로 스레드를 만들어 실행하는 방법 밖에 없다!
스레드의 생명주기(lifecycle) - 죽은 스레드는 다시 살릴 수 없다.
// 스레드의 생명주기(lifecycle) - 죽은 스레드는 다시 살릴 수 없다.
package com.eomcs.concurrent.ex4;
import java.util.Scanner;
public class Exam0111 {
public static void main(String[] args) {
System.out.println("스레드 시작시킴.");
Thread t = new Thread(() -> { // Runnable 구현체를 정의하고 생성한다.
for (int i = 0; i < 1000; i++) {
System.out.println("===> " + i);
}
System.out.println("스레드의 run() 실행 종료!");
});
t.start();
Scanner keyboard = new Scanner(System.in);
keyboard.nextLine(); // 스레드가 종료될 때까지 시간을 벌기 위함.
keyboard.close();
// 죽은 스레드 객체를 또 실행할 수 없다.
t.start(); // 예외 발생! ==> IllegalThreadStateException
System.out.println("main() 종료!");
}
}
=>
...
=> enter 를 치는 순간 예외 발생
스레드의 생명주기(lifecycle) - join()
// 스레드의 생명주기(lifecycle) - join()
package com.eomcs.concurrent.ex4;
public class Exam0120 {
public static void main(String[] args) throws Exception {
System.out.println("스레드 실행 전");
Thread t = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println("===> " + i);
}
});
t.start(); // 스레드를 생성하고 시작시킨다.
t.join(); // t 스레드가 종료될 때까지 "main" 스레드는 기다린다.
// 즉 t 스레드가 종료된 후 다음 코드를 실행한다.
System.out.println("스레드 종료 후");
// 스레드 종료 후 다시 시작시킨다면?
// => IllegalThreadStateException 발생!
// => 즉 종료된 스레드는 다시 running 할 수 없다.
// t.start();
}
}
ㄴ 종료된 스레드는 다시 running 할 수 없음
=>
스레드의 생명주기(lifecycle) - sleep()
// 스레드의 생명주기(lifecycle) - sleep()
package com.eomcs.concurrent.ex4;
public class Exam0130 {
public static void main(String[] args) throws Exception {
System.out.println("스레드 실행 전");
new Thread() {
@Override
public void run() {
System.out.println("Hello!");
}
}.start();
// 3초 동안 not runnable 상태로 만든다.
// => 즉 3초 동안 CPU가 놀고 있더라도 CPU를 사용하지 않는다.
// => 3초가 지나면(timeout) 다시 "main" 스레드는 CPU를 받아 실행할 수 있다.
// => sleep()을 호출하면 그 순간에 실행하는 스레드를 잠들게 한다.
Thread.sleep(3000); // milliseconds
System.out.println("스레드 실행 후");
}
}
ㄴ sleep 을 이용하여 not runnable 상태로 만들 수 있음
ㄴ Thread.sleep(3000) => 3초 동안 not runnable 상태로 만들 수 있음
=>
=>
ㄴ 3초가 지나면(timeout) 다시 "main" 스레드는 CPU를 받아 실행할 수 있음
=> sleep()을 호출하면 그 순간에 실행하는 스레드를 잠들게 할 수 있음
스레드의 생명주기(lifecycle) - running 상태 : CPU 쟁탈전(racing)
// 스레드의 생명주기(lifecycle) - running 상태 : CPU 쟁탈전(racing)
package com.eomcs.concurrent.ex4;
public class Exam0140 {
public static void main(String[] args) throws Exception {
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++)
System.out.printf("%s %d\n", this.getName(), i);
}
}
MyThread t1 = new MyThread("홍길동 =====>");
MyThread t2 = new MyThread("오호라 ------------>");
MyThread t3 = new MyThread("우헤헤 ##");
// 스레드를 시작시키는 순간 running 상태로 접어든다.
t1.start();
t2.start();
t3.start();
for (int i = 0; i < 1000; i++)
System.out.printf("main 스레드: %d\n", i);
}
}
=>
...
...
...
ㄴ 3개의 스레드 + 메인 스레드가 CPU 쟁탈전을 벌임
프로세스(스레드) 스케줄링
=> OS가 프로세스나 스레드에 CPU 사용을 배분하는 정책
1) Round-Robin 방식
- Windows 운영체제에서 사용하는 방식이다.
- 우선 순위 보다는 일정 시간 위주로 프로세스나 스레드에게 CPU를 배분하는 방식이다.
2) Priority + Aging 방식
- Unix나 Linux 운영체제에서 사용하는 방식이다.
- 우선 순위가 높은 프로세스나 스레드에게 CPU를 먼저 배분하는 방식이다.
- 우선 순위 배분 방식에서는 우선 순위가 낮은 경우 실행에서 소외되는 문제가 발생하기 때문에
우선 순위가 높은 프로세스나 스레드 때문에 실행 순서가 밀릴 때 마다
원래의 낮은 순위를 높임으로써(aging) 결국에는 모든 프로세스와 스레드의
실행을 완료할 수 있게 한다.
"컨텍스트 스위칭(context switching)"
- 동시에 여러 개의 프로세스나 스레드를 실행할 때 CPU 사용권을 뺏어 다른 프로세스나 스레드에게 주기 전에
현재까지 실행한 코드의 위치 정보를 저장해야 한다.
또한 CPU 사용권을 주기 전에 그 프로세스나 스레드가 이전에 어디까지 실행했었는지
이전 실행 위치 정보를 로딩해야 한다.
즉 실행 위치에 대한 정보를 저장하고 로딩하는 것을 말한다.
스레드의 생명주기(lifecycle) - 우선 순위 조회
// 스레드의 생명주기(lifecycle) - 우선 순위 조회
package com.eomcs.concurrent.ex4;
public class Exam0210 {
public static void main(String[] args) throws Exception {
// 스레드 우선 순위 정보
//
// => 스레드의 우선 순위 범위
System.out.printf("우선 순위 범위: %d ~ %d\n", //
Thread.MIN_PRIORITY, //
Thread.MAX_PRIORITY);
// => 스레드의 기본 우선 순위
System.out.printf("우선 순위 기본값: %d\n", Thread.NORM_PRIORITY);
// => "main" 스레드의 우선 순위 조회
System.out.printf("main 스레드 우선 순위: %d\n", //
Thread.currentThread().getPriority());
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++)
System.out.printf("%s %d\n", this.getName(), i);
}
}
MyThread t1 = new MyThread("t1");
// "t1" 스레드의 우선 순위 조회
// => "main" 스레드를 실행하는 동안 만든 스레드는 "main"의 자식 스레드라 부른다.
// => 자식 스레드는 부모 스레드의 우선 순위와 같은 값을 갖는다.
// 그래서 "t1" 스레드는 "main"의 우선 순위 값과 같다.
System.out.printf("%s 스레드 우선 순위: %d\n", t1.getName(), t1.getPriority());
}
}
=>
ㄴ 자식 스레드는 부모 스레드의 우선 순위 값과 같음
ㄴ t1 스레드는 main 스레드가 만든 스레드이므로 t1 스레드와 main 스레드는 우선순위가 같음
스레드의 생명주기(lifecycle) - 우선 순위 설정
// 스레드의 생명주기(lifecycle) - 우선 순위 설정
package com.eomcs.concurrent.ex4;
public class Exam0220 {
public static void main(String[] args) throws Exception {
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++)
Math.asin(38.567); // 시간 끌기 용. 왜? 부동소수점 연산은 시간을 많이 소요.
long endTime = System.currentTimeMillis();
System.out.printf("MyThread = %d\n", endTime - startTime);
}
}
// main 스레드의 우선 순위를 가장 작은 1로 설정한다.
Thread.currentThread().setPriority(1);
MyThread t1 = new MyThread("t1");
t1.setPriority(10);
// 유닉스 계열의 OS는 스케줄링에서 우선 순위를 고려하여 CPU를 배분한다.
// 따라서 프로그램을 짤 때 스레드의 우선 순위를 조정하는 방법에 의존하지 말라!
System.out.printf("main 스레드 우선 순위: %d\n", Thread.currentThread().getPriority());
System.out.printf("%s 스레드 우선 순위: %d\n", t1.getName(), t1.getPriority());
// t1 스레드 작업 시작
t1.start();
// main 스레드 작업 시작
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++)
Math.asin(38.567); // 부동 소수점 연산을 수행하는 코드를 넣어서 실행 시간을 약간 지연시킨다.
long endTime = System.currentTimeMillis();
System.out.printf("main = %d\n", endTime - startTime);
}
}
ㄴ 유닉스 계열의 OS는 스케줄링에서 우선 순위를 고려하여 CPU를 배분함
=> 그러나 Windows OS는 우선 순위를 덜 고려하여 CPU를 배분함
ㄴ 우선 순위를 조정하여 작업을 처리하도록 프로그램을 짜게 되면, 유닉스 계열에서 실행할 때는 의도한 대로 동작할지 모르지만,
윈도우에서는 의도대로 동작하지 않을 것임
=> 따라서 프로그램을 짤 때 스레드의 우선 순위를 조정하는 방법에 의존하지 말자