。゚(*´□`)゚。

코딩의 즐거움과 도전, 그리고 일상의 소소한 순간들이 어우러진 블로그

[네이버클라우드] 클라우드 기반의 개발자 과정 7기/웹프로그래밍

[NC7기-56일차(7월13일)] - 웹프로그래밍 37일차

quarrrter 2023. 7. 13. 16:43

멀티태스킹 

 


멀티태스킹(multi-tasking)

=> 동시에(concurrent 병행) 여러개의 app을 실행

실행 중인 app = process

프로세스안에는 ,, 들어있다.
실행중인 App
명령문

*CPU는 한 번에 한 명령 처리 => 여러 프로세스를 동시에 실행하기 위해서 돌아가면서 돌아가면서 명령문을 실행. => 실행 속도가 빠르기 때문에 가능. => 실제는 동시 실행이 아니라 병행, 순차적으로 빠르게 실행하는 것이다. => 동시에 실행되는 것처럼 보이는 것.  => 어떤 방법으롤 돌아가면서 실행하는것이 더 효과적일까!?  => 그 방법: 프로세스 스케줄링, 또는 CPU 스케줄링임.(돌아가면서 실행하는 것)

1클럭: CPU에 전기 땅~ 한 번 보내는거 . 코어 수가 많을 수록 더 빠르게 작업 가능 

서버에서 4기가 짜리르르 한 개 꼽는것보다 헤르츠가 낫더라도 코어수가 많은 게 좋음 , 헤르츠가 높으면 열이 더 발생해. 전기가 조금 흐를 수록 연기가 덜발생, 

CPU 한개- 책단개

클럭이 한 번 도는 동안 메모리에서 명령어를 가져와서 해석하고 처리함. 

헤르츠 Hz: 주파수 초당 실행하는 사이클 (6Hz면 1초당 6번) ( 3.2GHz: 초당 32억번)

PC의 두뇌: CPU

그래픽 카드와 모니터 연결되어있고 출력, CPU는 디코딩해서 담는거 까지만.

CPU 스케줄링

여러 프로세스를 돌아가면서 실행하는 알고리즘

Round Robin(윈도우 사용)

 

1. Round Robin(윈도우 사용)

일정한 단계로 시간을 쪼개서 프로세스를 순차적으로 실행

 

Priority + Aging (unix)

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의 부모는 없군 , ,!

// "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은 종료하지 않는다.
}


}

dead가 되면 다시 running으로 갈 수 없다 !!!!!!!!!!!!!!!

// 스레드의 생명주기(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();

  }

}

join

// 스레드의 생명주기(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 설치 

https://developer.hashicorp.com/vagrant/downloads?product_intent=vagrant

1. 프로젝트 폴더 생성  // PS C:\Users\bitcamp\vagrant\ubuntu>

2. 이미지 다운로드, vagrant init "이미지 이름"

### Variant Box(가상머신 이미지) 찾기

- [가상머신이미지 저장소 사이트](https://app.vagrantup.com/) 접속

Discover Vagrant Boxes - Vagrant Cloud (vagrantup.com)

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 : 프로세스 별 구분하는 번호

******