코드스테이츠 - 3회차 백엔드 부트캠프/Section 1

2022.09.16 금 - 스레드, 자바 가상 머신

곰돌이볼 2022. 9. 16. 22:05

📄 학습내용

스레드(Thread)

  • 스레드(Thread)의 기본 개념
    • 프로세스 : 현재 실행 중인 프로그램
    • 스레드 : 프로세스에서 실행되고 있는 코드의 흐름
    • 싱글 스레드 프로세스 : 스레드를 하나만 가진 프로세스
    • 멀티 스레드 프로세스 : 둘 이상의 스레드를 가진 프로세스
    • 메인 스레드 : 프로세스가 동작할 때 가장 먼저 동작하는 스레드(자바 프로그램 : main 메서드)
    • 작업 스레드 : 메인 스레드 이외의 스레드
  • 스레드(Thread)
    • 프로세스 구성 : 데이터, 컴퓨터 자원, 스레드
    • 스레드 : 프로세스가 확보한 자원과 컴퓨터 자원을 이용해서 코드 실행
  • 싱글 스레드 프로세스와 멀티 스레드 차이
    • 싱글 스레드만 가지는 싱글 스레드 프로세스는 메인 스레드만을 이용해서 작동 수행
    • 멀티 스레드를 가지는 멀티 스레드 프로세스는 여러 스레드를 이용해서 작업을 동시에 수행
    • 멀티 스레딩 : 여러 스레드가 동시 작업 수행하는 것

 

  • 스레드 생성 및 호출
    • run() 메서드 작성하기 : 작업 스레드가 수행한 코드를 run() 메서드 바디에 작성
    • 스레드 생성 방법 
      • Runnable 인터페이스를 구현한 객체에서 run() 메서드 구현해서 스레드 생성 후 실행
      • Tread 클래스를 상속받은 하위 클래스에서 run() 메서드 구현해서 스레드 생성 후 실행
      • Runnable 인터페이스의 익명 객체를 구현해서 스레드 생성 후 실행
      • Tread 클래스의 익명 객체를 구현해서 스레드 생성 후 실행
    • 스레드 호출 : run() 메서드 호출하기
public class Test2 {
    public static void main(String[] args) {
        // Runnable 인터페이스 구현
        Runnable run1 = new RunnableClass();
        Thread thread1 = new Thread(run1);
        thread1.run();

        // Tread 클래스를 상속받은 하위 클래스
        Thread thread2 = new ThreadSub();
        thread2.run();

        // Runnable 인터페이스의 익명 객체 구현
        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Runnable 인터페이스의 익명 객체 구현");
            }
        });
        thread3.run();

        // Tread 클래스의 익명 객체 구현
        Thread thread4 = new Thread() {
            public void run() {
                System.out.println("Tread 클래스의 익명 객체를 구현");
            }
        };
        thread4.run();
    }
}

// Runnable 인터페이스 구현
class RunnableClass implements Runnable {
    public void run() {
        System.out.println("Runnable 인터페이스 구현");
    }
}

// Tread 클래스를 상속받은 하위 클래스
class ThreadSub extends Thread {
    public void run() {
        System.out.println("Tread 클래스를 상속받은 하위 클래스");
    }
}

/* 실행 결과
Runnable 인터페이스 구현
Tread 클래스를 상속받은 하위 클래스
Runnable 인터페이스의 익명 객체 구현
Tread 클래스의 익명 객체를 구현
*/

 

  • 스레드 이름
    • 메인 스레드 이름 : main
    • 작업 스레드 이름 : Thread-n (정수 n은 0부터 시작)
    • 메서드
      • 스레드_참조객체.getName() : 스레드 이름 출력
      • 스레드_참조객체.setName(String 스레드_이름) : 스레드 이름 변경
      • Thread.currentThread() : 현재 실행 중인 스레드 주소값, getName() 메서드를 통해서 현재 실행 중인 스레드의 이름을 가져올 수 있음

 

  • 스레드 동기화
    • 스레드 동기화 : 멀티 스레드 프로세스가 동일한 데이터를 공유할 때 발생하는 오류를 방지하는 것
    • 임계 영역(Critical section) : 하나의 스레드만 실행되는 영역
    • 락(Lock) : 임계 영역에 접근할 수 있는 권한
      • 스레드는 임계 영역에서 다른 스레드의 방해를 받지않고 코드 실행
      • 다른 스레드는 락을 얻어서 임계 영역으로 접근 가능
    • 임계 영역 지정 키워드 : synchronized
      • 메서드 전체를 임계 영역으로 지정하는 방법
      • 특정 부분만을 임계 영역으로 지정하는 방법
        • this 객체의 락을 얻어서 임계 영역으로 진입 가능
// 메서드 전체를 임계 영역으로 지정하는 방법
class Class {
    public synchronized void method() { ... }
}

// 특정 부분만을 임계 영역으로 지정하는 방법
class Class {
    public void method() {
        synchronized (this) {
            ...
        }
    }
}

 

  • 스레드 상태
    • 생성 : 스레드 생성
    • 실행 대기 : 운영체제가 스레드를 실행할 때까지 대기
    • 실행 : 운영체제가 스레드 실행
    • 일시 정지 : 실행 대기로 가기 전까지 스레드 대기
    • 소멸 : 동작이 끝난 스레드 종료

 

  • 스레드 실행 제어 메서드
  • 스레드.start()
    • 생성된 스레드 → 실행대기 상태
  • Thread.yield()
    • 실행 상태 → 실행대기 상태
    • 다른 스레드에게 우선순위 양보
  • Thread.sleep(long milliSecond)
    • 실행 상태 → 일시정지 상태 변경
    • milliSecond만큼 대기
    • 일지정지 상태 → 실행대기 상태 조건
      • milliSecond 시간 경과
      • interrupt() 호출 : try ~ catch 문 사용하기
  • 호출_스레드.join()
    호출_스레드.join(long milliSecond)
    • 실행 상태 → 일시정지 상태
    • 호출한 스레드 작업이 종료될 때까지 대기
    • 일지정지 상태 → 실행대기 상태 조건
      • milliSecond 시간 경과
      • interrupt() 호출
      • 호출한 스레드가 모든 작업이 종료되거나 일시중지가 되었을 때
  • 일시정지_스레드.interrupt()
    • 일시정지 상태 → 실행대기 상태
  • wait() & notify()
    • notify() : 다른 스레드를 실행대기 상태로 변경
    • wait() : 자기 자신을 일시정지 상태로 변경
    • 두 스레드가 공유하는 데이터를 접근할 때 효율적

 

자바 가상 머신(JVM)

  • 자바 가상 머신(JVM, Java Virtual Machine)
    • 자바 소스 코드 해석 및 실행 프로그램
    • 프로그램이 운영체제에게 필요한 자원을 요청하는 방식 떄문에 프로그래밍 언어가 운영체제에 종속성을 가짐
    • 운영체제 별로  JVM이 개발되어 있어서 운영체제에 맞추어서 소스 코드 실행 가능
           → 자바와 운영체제가 독립적인 이유

 

  • JVM 구조을 이용한 소스 코드 해석 및 실행 과정
    1. 소스 코드 작성
    2. 컴파일 : 컴파일러가 소스 코드를 바이크 코드로 변환
    3. 메모리 로드 : 클래스 로더(Class Loader)가 바이트 코드를 JVM 내부로 불러 RDA에 적재
    4. 바이크 코드 실행
      • 일반적인 경우 : 인터프리터(Interpreter)가 코드 한 줄씩 기계어로 변역 및 실행
      • 자주 실해되는 코드인 경우 : JIT Compiler(Just-In-Time Complier)가 바이트 코드 전체를 기계어로 변역 및 실행

 

  • Runtime Data Area의 Stack과 Heap
    • Runtime Data Area(RDA) : 실행에 필요한 메모리 저장 공간
    • Stack Area : LIFO(Last In First Out), 메서드 프레임이 호출되는 순서대로 쌓이고 역순으로 메서드 실행
    • Heap Area : 단 하나의 영역만 존재, 객체, 인스턴스 변수, 배열 저장
      • Young 영역 : 많은 객체가 생성되었다가 소멸하는 공간
      • Old 영역 : Young 영역에서 오랫동안 상태가 유지된 객체들이 복사된 공간

 

  • 가비지 컬렉션(Garbage Collection)
    • Haep 영역에서 필요없는 객체를 제거해서 메모리를 자동으로 관리해주는 프로세스
    • Minor GC : Heap의 Young 영역에서 활동하는 가비지 컬렉터
    • Major GC : Heap의 Old 영역에서 활동하는 가비지 컬렉터
    • 동작 방식
      1. Stop The World : JVM이 가비지 컬렉션을 실행시키기 위해서 모든 작업 중단
      2. Mark and Sweep : 사용되지 않는 메모리 식별(Mark)사용되지 않는 메모리 해체(Sweep)

 

🧶 발생한 문제 및 해결방법

  • 문제점) 문자열의 처음과 끝부분 비교
  • 해결방법)
    • 문자열_객체.startsWith(비교_문자열) : 문자열이 비교_문자열로 시작하면 true 반환
    • 문자열_객체.endsWith(비교_문자열) : 문자열이 비교_문자열로 끝나면 true 반환

 

  • 문제점) Stream 형태로 List 객체 합치기
  • 해결방법) Stream.concat(List객체1.stream(), List객체2.stream()); // Stream 객체

 

  • 문제점) Stream 객체를 배열로 변경
  • 해결방법) stream객체.toArray(배열 객체 생성);
    • ex) stream객체.toArray(String[]::new);

 

 공부 난이도

스레드, JVM ☆☆☆☆★

 

🎡 페어리뷰

 

🌕 느낀점

  Stream이 어떻게 구성되어 있고, 다양한 Stream을 어떤 함수를 이용해야하는지 적용하는데 어려웠다. 함수 별로 반환값과 매개변수 타입을 제대로 알고 적용하는 것이 중요한 것 같다. 유어 클래스에는 자세히 나와있지 않아서 인텔리제이에서 내가 직접 코드를 작성해보고 구글링한 결과를 정리해서 블로그에 작성해야겠다. 오늘 페어 활동하면서 Stream 연습문제를 푼 것 빼고는 크게 어려운 부분은 없었다. 스레드 부분은 예전에 운영체제를 공부할 때 배워서 간단하게 넘어갈 수 있었다. 새롭게 알게된 것은 메서드의 기능이었다. 자바 가상 머신의 대략적인 구조를 통해서 소스 코드가 어떤 과정을 거쳐서 실행되는지 전반적으로 알게 되었다.