10장. 병렬 프로그래밍

4 minute read

목표

자바의 멀티쓰레드 프로그래밍에 대해 학습하세요. issue

학습할 것

Thread 클래스와 Runnable 인터페이스

Thread 클래스

  • 직접 해당클래스에 상속을 받아서 run을 구현함.
  • Thread 클래서에는 interrupt(), start() 이런 메소드 들을 오버라이딩 해서 자기가 원하는대로 재정의할 수 있다.
public class ThreadExample extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getId());
    }

    @Override
    public void interrupt() {
        // 내가 원하는거 할 수 있다!!!
        super.interrupt();
    }
}

Runnable 인터페이스

  • extents으로 상속하는 것이 아닌 interface에 있는 run을 구현
  • 익명 클래스를 통해서 구현할 수 있음. or 람다식을 통해서 코드 진행중에 정의 가능.
public class RunnableExample implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getId());
    }
}

---
public class RunnableExample2 {

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getId());
            }
        });
        
        thread.start();
    }
}

---
public class RunnableExample3 {

    public static void main(String[] args) {
        new Thread(() -> System.out.println(Thread.currentThread().getId())).start();
    }
}

쓰레드의 상태

해당 내용은 OS라는 과목을 공부하면서 나오는 개념이라고 생각한다.

  • Ready: 아직 cpu에게 해당 스케줄링되어지지 않아서 기달리고 있는 상태입니다.
  • Running: cpu에게 선택받아서 해당 작업을 진행하고 있는 상태입니다.
  • Waiting: io/네트워크와 같은 다른 작업을 기달리고 있는 상태입니다. cpu에게 할당되었다가 현재는 할당되지 않은 상태입니다.
  • Terminated: 모두 종료되어서 없어진 상태입니다.

쓰레드의 우선순위

쓰레드 실행에 있어서 순서를 부여할 수가 있다. 우선순위는 스케줄링을 해야될 때 필요한 개념이다.

여러 스케줄링 규칙이 있겠지만 대표적으로는 2가지 방법이 있을 것 같다.

  • 라운드 로빈
    • 우선순위를 똑같이 지정하여서 모든 쓰레드를 동일시하게 작업을 처리한다. 여러 쓰레드를 몇초씩 돌아가면서 실행하다고 생각하면 된다.
  • 우선순위 기반
    • 가장 우선순위가 높은 쓰레드를 실행시킨다. 실행 중 가장 높은 우순순위를 가진 쓰레드가 만들어지는 경우 해당 쓰레드를 실행시킨다.

Main 쓰레드

우리는 자바 프로그램을 동작시키기 위해서 반드시 메인이라는 메소드를 실행시켜야 됩니다.

맨 처음 jvm이 실행되고 쓰레드를 만들어서 main 메소드를 실행시키는 쓰레드를 main 쓰레드라고 한다.

우리가 어떤 쓰레드를 만들어도 해당 쓰레드가 처음이며 우리가 알게 모르게 실행되는 친구이다.

daemon thread

일반적으로 부모 쓰레드는 자식 쓰레드의 동작을 기달릴 수 있다. (설정이 필요합니다. join()과 같은 메소드 사용.)

부모 쓰레드가 죽었는데 자식 쓰레드는 기본적으로는 계속 동작합니다. 전체 프로그램 자체가 쓰레드가 있는 경우 프로그램이 종료되지 않습니다.

자식 쓰레드를 daemon thread로 만드는 경우 부모 쓰레드가 죽으면 자식 쓰레드도 같이 종료 시킬 수 있습니다.

...
thread.setDaemon(true);
thread.start();
...

동기화

여러 쓰레드가 동시에 실행되는데 있어서 서로 방해하지 않으면 큰 문제는 없다.

하지만 여러 쓰레드가 동시에 하나의 데이터에 접근할 수 있는 상황이라면 문제가 발생할 수 있다.

해당 데이터를 공유 변수, 공유 데이터라고 부를 수 있는데 해당 데이터의 일관성이 깨질 수 있다.

그런 코드의 영역을 크리티컬 세션(critical section), 해당 조건이 발생할 수 있다는 것을 race condition이라고 한다.

그러기 위해서 lock 이라는 개념이 만들어졌다.

synchronized

public class SynchronisedExample {

    public synchronized void printWithMethod() {
        System.out.println("hello world");
    }

    public void printWithBlock(Object commonVariable) {
        synchronized (commonVariable) {
            System.out.println("hello world");
        }
    }
}

method

함수 전체에 대한 synchronized 을 사용하여도 해당 메소드를 접근하는 쓰레드를 제한한다.

임계 영역을 해당 메소드 전체로 지정한 것이다.

block

해당 코드 블락에 대한 synchronized 을 사용하여도 해당 블럭에 대해서만 쓰레드 접근을 제한한다.

해당 요소에 장점을 특정 자원에 대한 락을 걸 수 있는 것.

락을 거는 부분에 최소화해서 지정하여 성능적으로 얻는 장점이 있을 수 있다.

Critical Path

동시에 실행하는 작업 중에 가장 오래 걸리는 작업.

성능적으로 Critical Path 를 판단하고 줄이게 되면 전체 프로그램의 대한 성능을 좋게 만들 수 있다.

계속 줄이게 되면 다른 작업이 Critical Path가 될 수 있다.

Critical Path-before Critical Path-after

데드락

두 개 혹은 여러 개의 프로세스(쓰레드)가 공유 자원을 가지고 다른 자원에 대한 락이 풀릴 때 까지 기달리게 되면서 프로그램이 멈추는 현상. 혹은 해당 로직이 돌아가지 않는 문제.

조건

  1. 상호배제(Mutual exclusion) : 프로세스들이 필요로 하는 자원에 대해 배타적인 통제권을 요구한다.
  2. 점유대기(Hold and wait) : 프로세스가 할당된 자원을 가진 상태에서 다른 자원을 기다린다.
  3. 비선점(No preemption) : 프로세스가 어떤 자원의 사용을 끝낼 때까지 그 자원을 뺏을 수 없다.
  4. 순환대기(Circular wait) : 각 프로세스는 순환적으로 다음 프로세스가 요구하는 자원을 가지고 있다.

교착상태 회피

자원이 어떻게 요청될지에 대한 추가정보를 제공하도록 요구하는 것으로 시스템에 circular wait가 발생하지 않도록 자원 할당 상태를 검사한다.

교착 상태 회피하기 위한 알고리즘으로 크게 두가지가 있다.

  1. 자원 할당 그래프 알고리즘 (Resource Allocation Graph Algorithm)
  2. 은행원 알고리즘 (Banker’s algorithm)

Ref

https://www.notion.so/ac23f351403741959ec248b00ea6870e

라이브 방송

책 추천

자바 병렬 프로그래밍

매우 유명한 저자가 글을 썼다능~~ (이펙티브 자바 저자)

방송 중

  • Runnable와 Thread 상속 중에 무엇을 언제 사용할 것이냐?
    • Thread 상속은 run() 말고도 다른 것들을 오버라이팅을 해야되는 경우
    • 아닌 경우 Runnable을 interface를 구현하면 됨.
  • stop() 메소드는 없다고 생각하면 됨.
    • 자바가 쓰레드를 종료하는 방법은 없음.
    • 데이터에 대한 불균형이 생길 수 있기 때문에 -> 데드락 요런 것들.
  • 쓰레드를 사용해야되는 경우
    • 리소스를 극한으로 쓰려고 할 때.
    • 그런데 우리가 이렇게 쓰지 않아도 되는 것은 스프링, 네티, 서블렛 이런 친구들이 다 만들어놨고 그 내부에서 다 돌아가고 있다.
  • 컨테이너 (톰켓, 등등) 같은 것은 커넥션 풀이라는 것을 가지고 있음.

  • deamon 쓰레드. 부모 쓰레드가 끝날 때 같이 끝나버림.

  • connection에는 그냥 io 커넥터와 nio 커넥터 2개가 있음. 요즘은 nio.

  • volatile: 오직 하나의 쓰레드에서만 접근할 수 있다고 정의되어 있어야함. 실제로 쓰기는 어려움.

  • 쓰레드는 코어 수 보다 많이 만들어봤자 의미없다.

  • actor model, stm model
    • concurrent program
    • STM, Clojure
    • ​Actor, Akka
  • critical path? 알아두면 좋음.
    • 동시에 실행되는 것들 중에 가장 오래 걸리는 것
    • 그 친구를 줄여야지 프로그램 속도가 빨라지는 것
    • 정확하게 시간을 측정해야 됨.
    • 가장 길게 걸리 애들을 줄이면 전체적인 업무 시간이 줄어듬.
    • 줄이다 보면 또 다른 것이 critical path가 된다.
  • race condition
    • 이런 용어도 알면 좋음.
  • Thread dump
    • visualVM 같은 거 쓰면 볼 수 있음.
    • 모든 쓰레드의 스냅샵
    • heap dump: 메모리 스냅샵을 씀.
  • free lunch is over
    • 무어의 법칙은 끝남. 이젠 클럭 수는 증가시키기 어려움
    • 이제는 코어수가 많아질 것이고 cpu가 많아 질것임.
    • 그래서 멀티프로그래밍 패러다임이 바뀌고 있다.

11주차 과제

Enum

  • 정의
  • 제공하는 메소드
  • Enum class
  • EnumSet

베스트 글 모음

https://sujl95.tistory.com/63

https://wisdom-and-record.tistory.com/48

https://blog.naver.com/hsm622/222212364489

https://catch-me-java.tistory.com/47

https://parkadd.tistory.com/48

https://www.notion.so/Thread-5fdb5d603a6a473186bf2738f482cedc

https://leemoono.tistory.com/26

https://www.notion.so/ac23f351403741959ec248b00ea6870e -> 이거는 꼭 보기를 추천함.

https://yadon079.github.io/2021/java%20study%20halle/week-10 -> 이것도 꼭 보세요

Leave a comment