멀티태스킹
멀티태스킹(multi-tasking)
=> 동시에(concurrent 병행) 여러개의 app을 실행
프로세스안에는 ,, 들어있다. |
실행중인 App |
명령문 |
*CPU는 한 번에 한 명령 처리 => 여러 프로세스를 동시에 실행하기 위해서 돌아가면서 돌아가면서 명령문을 실행. => 실행 속도가 빠르기 때문에 가능. => 실제는 동시 실행이 아니라 병행, 순차적으로 빠르게 실행하는 것이다. => 동시에 실행되는 것처럼 보이는 것. => 어떤 방법으롤 돌아가면서 실행하는것이 더 효과적일까!? => 그 방법: 프로세스 스케줄링, 또는 CPU 스케줄링임.(돌아가면서 실행하는 것)
1클럭: CPU에 전기 땅~ 한 번 보내는거 . 코어 수가 많을 수록 더 빠르게 작업 가능
서버에서 4기가 짜리르르 한 개 꼽는것보다 헤르츠가 낫더라도 코어수가 많은 게 좋음 , 헤르츠가 높으면 열이 더 발생해. 전기가 조금 흐를 수록 연기가 덜발생,
CPU 한개- 책단개
클럭이 한 번 도는 동안 메모리에서 명령어를 가져와서 해석하고 처리함.
헤르츠 Hz: 주파수 초당 실행하는 사이클 (6Hz면 1초당 6번) ( 3.2GHz: 초당 32억번)
PC의 두뇌: CPU
CPU 스케줄링
여러 프로세스를 돌아가면서 실행하는 알고리즘
1. Round Robin(윈도우 사용)
일정한 단계로 시간을 쪼개서 프로세스를 순차적으로 실행
2. Priority + Aging (unix)
우선 순위가 높은 프로세스를 먼저 실행
우선 순위가 낮은 경우 실행에서 뒤로 밀린다. => 밀릴 때마다 우선 순위를 1단계 높힌다. (Aging기법)
Context-Switching
프로세스 실행 정보(레지스터 값 등) 교환
* 기반 지식: cpu가 명령을 처리하는 과정(mechanism)
레지스터(숟가락 역할,,) : cpu에 내장된 메모리
1. 프로그램 카운터(pc) : 실행할 명령어가 들어있는 메모리주소
2. 메모리 주소 레지스터(MAR) : 가져올 명령어/데이터가 들어있는 메모리의 주소
3. 메모리 버퍼 레지스터(MBR) : 가져온 명령어/데이터를 보관
4. 명령어 레지스터(IR) : (기계)명령어를 두는 레지스터
5. ALU (입력) : 산술연산유닛 altimative 어쩌구 unit. 220에 load가 있어서 ALU에 3번, 17번이 올라감.
다음 230 실행할 땐 load할거 없으니까 바로 add 실행.
프로세스 다른거 실행하려면 레지스터를 다시 셋팅해야함. , 메모리어딘가에 어디까지 했는지 저장해놔야하고 다시 불러오렬면 또 시간 걸림 (실행환경 교환해야함 == context switching)
L1 cache: 명령어 임시 보관
L2 cache: 데이터 임시 보관
멀티태스킹 프로그래밍
1. 프로세스 복제 (fork) 부모 -> 자식 (멀티 프로세싱)
특징:
프로세스 상태 그대로 복제 - > 메모리 그대로 복제 -> 메모리 낭비가 심하다
부모-자식 관계는 독립적이다. -> 프로세스 삭제가 번거롭다.
100명이 동시에 접속하면 프로세스 100개 복제되고; 100번을 강제 종료해야한다.
(메모리 낭비 극복, 부모- 자식 간 종속 관계)
2. 스레드 도입 (멀티 스레딩)
특징:
프로세스의 heap과 명령을 공유 => 메모리 절약
스레드는 프로세스에 종속된다 -> 프로세스 종료 -> 종속된 스레드 모두 종료
*fork() 예
[process 1 ]
int main() {
int i = 0;
pid_t processId = 0;
processId = fork(); // 현재 실행 실행 위치에서 프로세스 복제
for (i = 0; i < 10000; i++) {
printf("[%d] ==> %d\n", processId, i);
int temp = rand() * rand();
}
return 0;
}
fork()를 만나면 프로세스 복제됨
int main() {
int i = 0;
pid_t processId = 0;
processId = fork(); // 현재 실행 실행 위치에서 프로세스 복제
for (i = 0; i < 10000; i++) {
printf("[%d] ==> %d\n", processId, i);
int temp = rand() * rand();
}
return 0;
}
프로세스가 복제되기때문에 stack & heap이 그대로 복제된다.
2. 스레드 (thread)
스레드: 하나의 주제에 대해 회원들이 게시판에 올린 일련의 의견 , 실, 꿰다.
JVM (start() ) -> main Thread -> call main() : 실행흐름: 한 개의 실처럼 연결되어 있다. 절대 중간에 끊기지않음
main()을 호출하는 것은 jvm이 아니다.
main() 중간에 new Thread().start()면 Thread 가 run()을 call 함. (call 하는 순간 새 실행흐름이 생성 및 실행됨
OS는 프로세스나 스레드를 동등한 자격으로 대우한다. cput 스케줄링할때 차별을 두지 않음.
=> 스레드는 프로세스와 동일한 자격으로 "cpu 쟁탈전에 참여 (cpu racing)
os가 어딜 실행시킬지(메모리를 할당할지 고름)그래서 왔다갔다 출력
단 프로세스를 종료하면 종속된 스레드도 종루됨.
스레드를 만든 스레드는 부모 스레드, 만들어진 스레드: 자식 스레드
Thread와 코드공유
main Tread - > main() 콜 ~~~~~~~~~ T1 -> run() 콜~~~~~~~~~ T2 -> run() 콜 ~~~~~~~~~ |
JVM STACK
1. 각 스레드 별로 존재한다.
2. 스레드가 메서드를 호출할 때마다 로컬 변수를 각자의 stack 영역에 생성한다.
JVM 메모리 생김새
JVM STACK은 thread 마다 존재, method area랑 heap은 공유 !
메서드가 로컬변수를 호출? 만들때마다 JVMstack에 쌓임,, ?
// JVM은 여러 개의 스레드를 실행한다.
// main() 호출도 별도의 스레드가 실행한다.
// 확인해보자!
// 이 순간 실행 중인 흐름이 무엇인지 알고 싶다면?
Thread t = Thread.currentThread();
System.out.println("실행 흐름명 = " + t.getName());
// 실행 흐름을 전문적인 용어로 "Thread(실 타래)"라 부른다.
// JVM이 실행될 때 main() 메서드를 호출하는 실행 흐름(스레드)의 이름은 "main"이다.
// JVM의 스레드 계층도:
// main(T)
// 스레드 그룹
Thread main = Thread.currentThread();
// 스레드는 그룹에 소속되기도 한다.
// 현재 스레드의 소속 그룹을 알고 싶다면?
ThreadGroup group = main.getThreadGroup();
System.out.println("그룹명 = " + group.getName());
// main() 메서드를 호출하는 스레드는 "main" 스레드이고,
// "main" 스레드가 소속된 그룹은 "main" 그룹이다.
}
}
// JVM의 스레드 계층도:
// main(TG)
// => main(T)
// 스레드 그룹에 소속된 스레드들
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());
// 스레드 그룹에 소속된 하위 그룹들
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)
// => 다른 하위 그룹은 없다!
// 스레드 그룹의 부모 그룹
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(),
// JVM의 스레드 계층도:
// system(TG)
// => main(TG)
// ...=> main(T)
// "system" 스레드 그룹의 자식 그룹들
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" 스레드 그룹에 소속된 스레드들
Thread main = Thread.currentThread();
ThreadGroup mainGroup = main.getThreadGroup();
ThreadGroup systemGroup = mainGroup.getParent();
Thread[] arr = new Thread[100];
int count = systemGroup.enumerate(arr, false);
System.out.println("system 스레드 그룹에 소속된 스레드들:");
for (int i = 0; i < count; i++) {
System.out.println(" =>" + arr[i].getName());
// JVM의 스레드 계층도:
// system(TG)
// => Reference Handler(T)
// => Finalizer(T)
// => Signal Dispatcher(T)
// => Attach Listener(T)
// => Notification Thread(T)
// => main(TG)
// ...=> main(T) : main() 메서드를 호출한다.
// => InnocuousThreadGroup(TG)
// JVM의 전체 스레드 계층도
// JVM의 최상위 스레드 그룹인 system의 계층도 출력하기
Thread mainThread = Thread.currentThread();
ThreadGroup mainGroup = mainThread.getThreadGroup();
ThreadGroup systemGroup = mainGroup.getParent();
printThreads(systemGroup, "");
}
static void printThreads(ThreadGroup tg, String indent) {
System.out.println(indent + tg.getName() + "(TG)");
// 현재 스레드 그룹에 소속된 스레드들 출력하기
Thread[] threads = new Thread[10];
int size = tg.enumerate(threads, false);
for (int i = 0; i < size; i++) {
System.out.println(indent + " ==> " + threads[i].getName() + "(T)");
}
// 현재 스레드 그룹에 소속된 하위 스레드 그룹들 출력하기
ThreadGroup[] groups = new ThreadGroup[10];
size = tg.enumerate(groups, false);
for (int i = 0; i < size; i++) {
printThreads(groups[i], indent + " ");
// JVM의 스레드 계층도: (Oracle JDK 17 기준)
// system(TG)
// ==> Reference Handler(T)
// ==> Finalizer(T)
// ==> Signal Dispatcher(T)
// ==> Attach Listener(T)
// ==> Notification Thread(T)
// ==> main(TG)
// ==> main(T)
// ==> InnocuousThreadGroup(TG)
// ==> Common-Cleaner(T)
스레드 만들기
스레드는 운영체제가 왔다갔다하면서 실행시키는 거임. jvm은 아무런 권한이 없음.
똑같은 자바프로그램이더라도 OS마다 cpu 스케줄링 방식이 째금씩 달라서 오묘하게 다름. ;
1. 스레드 상속 받기.
2. 인터페이스 Runnable를 구현한 MyRunnable <= MyRunnable 은 thread가 아니고 스레드가 실행할 코드를 갖고 있을 뿐이다.
virtual thread
os 에서 스레드 만드는거라 jvm이 통제하기 힘들었음, 디버깅이나 스레드 모니터링이 완벽하게 안 됐음.
OS커널의 스레드 갯수는 한정되어 있고 생성과 유지 비용이 꽤 비싸다.(메모리를 많이 쓴다)
이 때문에 플랫폼 스레드를 효율적으로 사용하기 위해 스레드 풀을 만들어서 사용해왔다.
한번 만든 스레드를 삭제하지 않구 재워서 나중에 다시 깨우는거 : 스레드 풀
전통적인 os스레드 = 플랫폼 스레드. 운영체제 스레드를 랩핑한것.
하드웨어 성능을 잘 활용하는 높은 처리량 서버를 작성하기 위해 가상 스레드 나온댄다 ~~!!!
// 스레드 만들기 I - Thread를 상속 받기
package com.eomcs.concurrent.ex3; public class Exam0110 { public static void main(String[] args) { // 1) Thread 클래스를 상속 받아 정의하기 // => 구현하기 편하다. // => 그런데 다중 상속이 불가능하기 때문에 다른 클래스를 상속 받을 수 없다. // => 즉 MyThread가 다른 클래스를 상속 받으면서 스레드가 될 순 없다. // 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); } } } // CPU 사용을 스레드에게 배분할 때, 스레드를 생성한 순서대로 배분하지는 않는다. // OS의 CPU 스케줄링 정책에 따라 스레드가 실행된다. // 즉 JVM에서 스레드를 실행하는 것이 아니라 OS가 실행한다. // 결론! // => 똑 같은 자바의 스레드 코드가 OS에 따라 실행 순서가 달라질 수 있다. // // 우선 순위로 조정하면 되지 않나요? 윈도우에선 잘 안 됨. 걍 쓰지말기. 리눅스에서 하면 잘 되긴 함 . 운영체제에 영향을 끼치는 건 쓰지말깅 // => Windows OS의 경우 우선 순위(priority) 값이 실행 순서나 실행 회수에 큰 영향을 끼치지 않는다. // 그래서 우선 순위의 값을 조정하여 스레드의 실행 회수를 조정하려 해서는 안된다. // => 왜? OS에 따라 실행 정책이 다르기 때문이다. // => 그냥 특정 코드를 동시에 실행하고 싶을 때 스레드를 사용한다고 생각하라! |
// Thread를 상속 받기 - 익명 클래스로 구현하기
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
public static void main(String[] args) { // 2) Runnable 인터페이스를 구현하기 // => 실무에서 스레드를 만들 때 많이 사용한다. // => 인터페이스를 구현하는 것이기 때문에 다른 클래스를 상속 받을 수 있다. // => 직접적으로 스레드가 아니기 때문에 실행할 때는 Thread의 도움을 받아야 한다. 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 - 익명 클래스로 구현하기
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)로 구현하기
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("프로그램 종료?"); } } |
스레드의 생명주기
// 스레드의 생명주기(lifecycle)
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은 종료하지 않는다. } } |
// 스레드의 생명주기(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() 종료!");
}
}
// 스레드의 생명주기(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();
}
}
// 스레드의 생명주기(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("스레드 실행 후");
}
}
// 스레드의 생명주기(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 상태로 접어든다.
// running 상태는 실행하고 있는 상태 뿐만 아니라,
// CPU를 받을 수 있는 상태이기도 하다.
// => CPU는 OS의 관리 정책(CPU Scheduling)에 따라 스레드나 프로세스에 배분된다.
// 물론 OS가 CPU를 배분한 후 임의시간 후에
// 다시 회수하여 다른 스레드(현재 스레드 포함)나 프로세스에 배분한다.
// 때에 따라서 같은 스레드가 연속해서 배분 받는 경우도 있을 것이다.
//
t1.start();
t2.start();
t3.start();
for (int i = 0; i < 1000; i++)
System.out.printf("main 스레드: %d\n", i);
}
}
// 프로세스(스레드) 스케줄링
// => OS가 프로세스나 스레드에 CPU 사용을 배분하는 정책
// 1) Round-Robin 방식
// - Windows 운영체제에서 사용하는 방식이다.
// - 우선 순위 보다는 일정 시간 위주로 프로세스나 스레드에게 CPU를 배분하는 방식이다.
// 2) Priority + Aging 방식
// - Unix나 Linux 운영체제에서 사용하는 방식이다.
// - 우선 순위가 높은 프로세스나 스레드에게 CPU를 먼저 배분하는 방식이다.
// - 우선 순위 배분 방식에서는 우선 순위가 낮은 경우 실행에서 소외되는 문제가 발생하기 때문에
// 우선 순위가 높은 프로세스나 스레드 때문에 실행 순서가 밀릴 때 마다
// 원래의 낮은 순위를 높임으로써(aging) 결국에는 모든 프로세스와 스레드의
// 실행을 완료할 수 있게 한다.
//
// "컨텍스트 스위칭(context switching)"
// - 동시에 여러 개의 프로세스나 스레드를 실행할 때
// CPU 사용권을 뺏어 다른 프로세스나 스레드에게 주기 전에
// 현재까지 실행한 코드의 위치 정보를 저장해야 한다.
// 또한 CPU 사용권을 주기 전에 그 프로세스나 스레드가 이전에 어디까지 실행했었는지
// 이전 실행 위치 정보를 로딩해야 한다.
// 즉 실행 위치에 대한 정보를 저장하고 로딩하는 것을 말한다.
// 스레드의 생명주기(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()); // 우선 순위가 높으면 CPU 사용 배분을 좀 더 자주 받는다. // => 스레드는 JVM에서 관리하는 것이 아니라 OS가 관리한다. // => 즉 OS의 스레드를 이용하는 것이다. // => 따라서 우선 순위에 따라 실행 스케줄을 어떻게 관리할지는 OS에 따라 다르다. // => Windows OS는 우선 순위를 크게 고려하지 않는다. // 그래서 Windows에서 실행할 때는 우선 순위에 영향을 적게 받을 것이다. // => Unix, Linux 계열 OS는 우선 순위를 고려한다. // 그래서 이런 OS에서 실행할 때는 우선 순위에 영향을 받을 것이다. // // 주의! // => Java 의 캐치프레이즈가 "Write Once, Run Anywhere!" 이다. // => 즉 OS에 상관없이 동일하게 동작하게 만드는 것이 자바의 목적이다. // => 그런데 우선 순위에 따라 실행률이 달라지고, // OS 마다 차이가 난다면, 자바의 목적에 부합하는 것이 아니다. // => 그래서 가능한 OS에 영향을 덜 받는 방식으로 코딩해야 한다. // => 이런 이유로 스레드를 다룰 때 우선 순위를 고려하는 방식으로 // 프로그래밍을 하지 말라! } } |
쓰지 말기
// 스레드의 생명주기(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를 배분한다.
// 그러나 Windows 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);
}
}
하이퍼 바이저
가상 머신 모니터라고도 하는 하이퍼바이저는 가상 머신(VM)을 생성하고 실행하는 프로세스
버전1
네이티브 또는 베어메탈 하이퍼바이저라고도 부르며, 하드웨어에 직접 설치되어 하드웨어를 게스트 OS를 설치할 수 있는 여러 개의 가상 머신으로 분할 //ex 네이버클라우드
버전2
호스트 하이퍼바이저라고도 부르며 호스트의 운영 체제 내에 설치되기 때문에 하이퍼바이저 관리 콘솔이 필요하지 않다는 장점
vagrant 로 리눅스 여러개 구축 , ?
vagrant 설치
1. 프로젝트 폴더 생성 // PS C:\Users\bitcamp\vagrant\ubuntu>
2. 이미지 다운로드, vagrant init "이미지 이름"
3. Box 다운로드 및 VM 생성, 실행
vagrant up
4. 접속
vagrant ssh
5. 나가기: exit
6. 가상머신 종료 : vagrant halt
7. 삭제: vagrant destroy
지웠어도 이전에 받은 기록이 캐시에 남아있어서 그냥 다시 up 하면 됨.
우분투에서는 현재경로를 몰라서 ./를 붙여야함
******
c에도 타입이 잇음
char, int(운영체제의 비트를 따른다.16비트컴퓨터일 때 - 2 , 32 - 4 , 64 - 8바이트임), long(4byte), float, double ...
pid_t로 int의 타입을 정해서 os에 상관없이 같은 값을 줌. if (16비트 os일때) { } else if (32비트 os일때) { } 이렇게 구
pid_t a; : 운영체제에 상관없이
PID : 프로세스 별 구분하는 번호
******
'[네이버클라우드] 클라우드 기반의 개발자 과정 7기 > 웹프로그래밍' 카테고리의 다른 글
[NC7기-58일차(7월17일)] - 웹프로그래밍 39일차 (0) | 2023.07.17 |
---|---|
[NC7기-57일차(7월14일)] - 웹프로그래밍 38일차 (0) | 2023.07.14 |
[NC7기-55일차(7월12일)] - 웹프로그래밍 36일차 (0) | 2023.07.12 |
[NC7기-54일차(7월11일)] - 웹프로그래밍 35일차 (0) | 2023.07.11 |
[NC7기-53일차(7월10일)] - 웹프로그래밍 34일차 (0) | 2023.07.10 |