thread

Thread

μ“°λ ˆλ“œλΌλŠ” κ°œλ…μ„ μ΄ν•΄ν•˜κΈ° μœ„ν•΄μ„œλŠ” ν”„λ‘œμ„ΈμŠ€μ™€ 병행성(Concurrency)의 κ°œλ…μ„ μ•Œλ©΄ 더 쒋을 것같닀.

ν”„λ‘œμ„ΈμŠ€

ν”„λ‘œμ„ΈμŠ€μ˜ 사전적 μ˜λ―ΈλŠ” μ»΄ν“¨ν„°μ—μ„œ μ‹€ν–‰λ˜κ³  μžˆλŠ” 컴퓨터 ν”„λ‘œκ·Έλž¨μ„ λ§ν•œλ‹€. μ—¬λŸ¬ 개의 ν”„λ‘œμ„Έμ„œλ₯Ό μ‚¬μš©ν•˜λŠ” 것을 λ©€ν‹° ν”„λ‘œμ„Έμ‹±μ΄λΌκ³  ν•˜λ©°, 같은 μ‹œκ°„μ— μ—¬λŸ¬ 개의 ν”„λ‘œκ·Έλž¨μ„ λ„μš°λŠ” μ‹œλΆ„ν•  방식을 λ©€ν‹°νƒœμŠ€ν‚Ή 이라고 ν•œλ‹€.

νŠΉμ§•

  • ν”„λ‘œκ·Έλž¨μ΄ μ‹€ν–‰λ˜λŠ” λ‹¨μœ„

  • λͺ¨λ“  ν”„λ‘œκ·Έλž¨μ€ μ‹€ν–‰ 될 λ•Œ ν•˜λ‚˜ μ΄μƒμ˜ ν”„λ‘œμ„ΈμŠ€λ₯Ό κ°–λŠ”λ‹€.

  • 각 ν”„λ‘œμ„ΈμŠ€λŠ” λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€μ˜ λ³€μˆ˜λ‚˜ 자료 ꡬ쑰에 μ ‘κ·Όν•  수 μ—†λ‹€.

  • 각 ν”„λ‘œμ„ΈμŠ€λŠ” λ³„λ„μ˜ μ£Όμ†Œκ³΅κ°„μ—μ„œ μ‹€ν–‰λœλ‹€.

  • 컴퓨터 λ©”λͺ¨λ¦¬μ— μ μž¬λ˜μ–΄ μ‹€ν–‰λ˜μ–΄ μžˆλŠ” ν”„λ‘œκ·Έλž¨μ„ λ§ν•œλ‹€.

  • ν”„λ‘œμ„ΈμŠ€μ˜ μƒνƒœλŠ” 생성, μ‹€ν–‰, μ€€λΉ„, λŒ€κΈ°, μ’…λ£Œκ°€ μžˆλ‹€.

병행성 (Concurrency) == λ™μ‹œμ„±

병렬성과 ν—·κ°ˆλ¦΄ 수 μžˆλŠ”λ° 병행성은 λ™μ‹œμ— μ‹€ν–‰λ˜λŠ” 것 처럼 λ³΄μ΄λŠ” κ²ƒμœΌλ‘œ μ‹±κΈ€μ½”μ–΄μ—μ„œλŠ” μ‹œλΆ„ν• κΈ°λ²•μœΌλ‘œ μˆ˜ν–‰ν•  수 있고 λ©€ν‹°μ½”μ–΄μ—μ„œλŠ” 물리적으둜 λ³‘λ ¬λ‘œ λ™μž‘ν•  수 μžˆλ‹€.

병렬성(Parallelism)은 μ‹€μ œλ‘œ λ™μ‹œμ— μž‘μ—…μ΄ μ²˜λ¦¬λ˜λŠ” κ²ƒμœΌλ‘œ λ©€ν‹°μ½”μ—μ—μ„œλ§Œ λ™μž‘μ΄ κ°€λŠ₯ν•˜λ‹€.

μ“°λ ˆλ“œ

ν”„λ‘œμ„ΈμŠ€μ˜ νŠΉμ •ν•œ μˆ˜ν–‰ 경둜둜 ν”„λ‘œμ„ΈμŠ€κ°€ ν• λ‹Ή 받은 μžμ›μ„ μ΄μš©ν•˜λŠ” μ‹€ν–‰μ˜ λ‹¨μœ„λΌκ³  ν•  수 μžˆλ‹€.

ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€μ—μ„œ μ—¬λŸ¬κ°œμ˜ μŠ€λ ˆλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 것을 λ©€ν‹° μŠ€λ ˆλ”©μ΄λΌκ³  ν•œλ‹€.

νŠΉμ§•

  • ν”„λ‘œμ„ΈμŠ€ λ‚΄μ—μ„œ μŠ€νƒ/λ ˆμ§€μŠ€ν„°λ§Œ 각각 ν• λ‹Ή λ°›κ³  λ‚˜λ¨Έμ§€ λ©”λͺ¨λ¦¬ μ˜μ—­μ€ κ³΅μœ ν•œλ‹€. μŠ€νƒμ€ 각각 ν• λ‹Ήν•˜λŠ” 이유 : μŠ€λ ˆλ“œλŠ” ν•œ κΈ°λŠ₯을 μˆ˜ν–‰ν•˜λŠ” 독립적인 λ‹¨μœ„λ‘œμ¨ μ΅œμ†Œν•œμ˜ λ©”λͺ¨λ¦¬ μ˜μ—­(μŠ€νƒ)은 각각 ν• λ‹Ήν•΄μ•Ό λ…λ¦½μ μœΌλ‘œ μˆ˜ν–‰μ΄ κ°€λŠ₯ν•˜λ‹€.

Thread ν΄λž˜μŠ€μ™€ Runnable μΈν„°νŽ˜μ΄μŠ€

μžλ°”μ—μ„œ μ“°λ ˆλ“œλ₯Ό μž‘μ„±ν•˜λŠ” λ°©λ²•μ—λŠ” Thread ν΄λž˜μŠ€μ™€ Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” 두가지 방법이 μžˆλ‹€.

Thread 클래슀 extends

public class Example {
  public static void main(String[] args){
    ExampleThread th1 = new ExampleThread();
    Thread th2 = new Thread(){
      @Override
      public void run(){
        System.out.println("thread2");
      }
    }

    th1.start();
    th2.start();
  }

  class ExampleThread extends Threads{
    @Override
    public void run(){
      System.out.println("Thread 1");
    }
  }
}

Thread 클래슀λ₯Ό ν™•μž₯ν•˜μ—¬ μƒμ„±ν•˜λŠ” λ°©λ²•μœΌλ‘œλŠ” thread클래슀λ₯Ό 상속받아 μƒμ„±ν•˜λŠ” 방법인 th1κ³Ό th2와 같이 읡λͺ… 객체λ₯Ό μ‚¬μš©ν•˜μ—¬ μƒμ„±ν•˜λŠ” 방법 2가지 κ°€ μ‘΄μž¬ν•˜κ³  λ‘λ²ˆμ§Έλ°©λ²•μΈ 읡λͺ… 클래슀λ₯Ό μ΄μš©ν•˜λŠ” 방법은 μ“°λ ˆλ“œκ°€ 였직 ν•œλ²ˆλ§Œ μˆ˜ν–‰ λ λ•Œ μ‚¬μš©ν•˜λŠ” 방법이닀.

Runnable μΈν„°νŽ˜μ΄μŠ€ implements

public class Example {
  public static void main(String[] args){
    Runnable r = new ExampleThread();
    Thread th1 = new Thread(r);
    //Thread th1 = new Thread(new ExampleThread());

    Thread th2 = new Thread(()->{
      System.out.println("thread2");
    });

    th1.start();
    th2.start();
  }

  class ExampleThread implements Runnalbe{}
    @Override
    public void run(){
      System.out.println("Thread 1");
    }
  }
}

Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ΄μš©ν•œ Thread생성방법도 두가지가 μ‘΄μž¬ν•œλ‹€.

  1. Runnalbe μΈν„°νŽ˜μ΄μŠ€λ₯Ό implementsν•œ classλ₯Ό μƒμ„±μž λ§€κ°œλ³€μˆ˜λ‘œ μ΄μš©ν•˜μ—¬ μƒμ„±ν•˜λŠ” 방법

  2. Runnable μΈν„°νŽ˜μ΄μŠ€λŠ” runλ©”μ„œλ“œλ°–μ— μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 점을 μ΄μš©ν•˜μ—¬ λžŒλ‹€λ₯Ό μ΄μš©ν•΄ λ°”λ‘œ runλ©”μ„œλ“œλ₯Ό μ •μ˜ν•˜μ—¬ μƒμ„±ν•˜λŠ” 방법

차이점

  1. Thread 클래슀λ₯Ό extendsν•˜λŠ” 방법은 classλ₯Ό μƒμ†λ°›λŠ” 것인 만큼 Thread의 λ‹€λ₯Έ λ©”μ„œλ“œλ“€μ„ 이용이 κ°€λŠ₯ν•˜μ§€λ§Œ λ‹€λ₯Έ classλ₯Ό 상속받지 λͺ»ν•˜μ§€λ§Œ, Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” 것은 λ‹€λ₯Έ classλ₯Ό 상속받을 수 있고 쑰금 더 객체 지ν–₯적이닀.

  2. Thread클래슀λ₯Ό μƒμ†λ°›λŠ” classλŠ” run λ©”μ„œλ“œκ°€ μ’…λ£Œλ˜λ©΄ 가비지 μ»¬λ ‰μ…˜μ΄ 되기 λ•Œλ¬Έμ— μž¬μ‚¬μš©μ΄ λΆˆκ°€λŠ₯ν•˜μ§€λ§Œ, Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 방법은 Thread ν΄λž˜μŠ€λŠ” 가비지 μ»¬λž™μ…˜μ΄ λ˜λ”λΌλ„ κ΅¬ν˜„μ²΄λŠ” 가비지 μ»¬λž™μ…˜μ΄ λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μž¬μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€.

  3. Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 경우 Thread클래슀의 λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜κ³  μ‹Άλ‹€λ©΄ Thread 클래슀의 static λ©”μ„œλ“œμΈ currentThread()λ₯Ό ν˜ΈμΆœν•˜μ—¬ μ“°λ ˆλ“œμ˜ μ°Έμ‘°λ₯Ό μ–»μ–΄μ•Ό μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€.

class ThreadA extends Thread  {
public void run() {
  System.out.println(getName());
}

class ThreadB implements Runnable {
  public void run() {
    System.out.println(Thread.currentThread().getName());
  }
}

μ“°λ ˆλ“œμ˜ μƒνƒœ

Start와 run

start()λŠ” NEWμƒνƒœμ˜ μŠ€λ ˆλ“œλ₯Ό RUNNABLEμƒνƒœλ‘œ λ§Œλ“€μ–΄μ£ΌλŠ” λ©”μ„œλ“œλ‘œ 싀행될 수 μžˆλŠ” λŒ€κΈ° 큐에 λ„£μ–΄μ£ΌλŠ” μ—­ν• (call stack 생성)

run()은 μŠ€λ ˆλ“œκ°€ μŠ€μΌ€μ€„λŸ¬μ— μ˜ν•΄ μ‹€μ œλ‘œ μ‹€ν–‰λ λ•Œ μ‹€ν–‰ν•  λ©”μ„œλ“œμ΄κ³  start()없이 run만 μ‹€ν–‰ν•˜κ²Œ λœλ‹€λ©΄ override된 λ©”μ„œλ“œλ₯Ό κ·ΈλŒ€λ‘œ ν˜ΈμΆœν•  뿐 ν•΄λ‹Ή ThreadλŠ” RUNNABLEμƒνƒœκ°€ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— 메타데이터가 μ‹€ν–‰λŒ€κΈ° 큐에 λ“€μ–΄μžˆμ§€ μ•Šμ•„ μ›ν•˜λŠ” λŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€.

μ“°λ ˆλ“œ μƒνƒœ μ œμ–΄ λ©”μ„œλ“œ

Thread ν΄λž˜μŠ€μ— μ •μ˜

Object ν΄λž˜μŠ€μ— μ •μ˜

νŠΉμ • 객체에 λŒ€ν•œ κ²ƒμ΄λ―€λ‘œ μœ„μ˜ λ©”μ„œλ“œλ‘Έ λ‹€λ₯΄κ²Œ Objectν΄λž˜μŠ€μ— μ •μ˜λ˜μ–΄ μžˆμ–΄ λͺ¨λ“  κ°μ²΄μ—μ„œ 호좜이 κ°€λŠ₯ν•˜κ³  동기화 λΈ”λ‘λ‚΄μ—μ„œλ§Œ μ‚¬μš©ν•  수 μžˆλ‹€.

μ“°λ ˆλ“œμ˜ μš°μ„ μˆœμœ„

μ—¬λŸ¬κ°œμ˜ μ“°λ ˆλ“œκ°€ λ™μž‘ν•˜λ©° 병행성을 μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄ μ‹œλΆ„ν• μ„ ν•˜μ—¬ μŠ€μΌ€μ€„λ§μ„ 톡해 μŠ€λ ˆλ“œλ₯Ό λ™μž‘ μ‹œν‚€λŠ”λ° 이λ₯Ό μœ„ν•΄, μžλ°”λŠ” μ“°λ ˆλ“œ ν΄λž˜μŠ€μ— μ–΄λ–€ μ“°λ ˆλ“œλ₯Ό 더 μ€‘μš”ν•˜κ²Œ μƒκ°ν•˜μ—¬ λ™μž‘μ‹œν‚¬μ§€ μ§€μ •ν•˜μ—¬ 더 λ§Žμ€ μž‘μ—…μ‹œκ°„μ„ 갖도둝 ν•  수 있게 ν•΄μ£ΌλŠ” 멀버 λ³€μˆ˜λ₯Ό κ°–κ³  μžˆλ‹€.

μš°μ„ μˆœμœ„μ˜ λ²”μœ„λŠ” 1~10이며 μˆ«μžκ°€ λ†’μ„μˆ˜λ‘ μš°μ„ μˆœμœ„κ°€ λ†’κ³ , 메인 λ©”μ„œλ“œλ₯Ό μˆ˜ν–‰ν•˜λŠ” μ“°λ ˆλ“œμ˜ μš°μ„ μˆœμœ„κ°€ 5μ΄λ―€λ‘œ mainλ©”μ„œλ“œ λ‚΄μ—μ„œ μƒμ„±ν•˜λŠ” μ“°λ ˆλ“œμ˜ μš°μ„ μˆœμœ„λŠ” 기본값이 5κ°€ λœλ‹€.

μ“°λ ˆλ“œμ˜ μš°μ„ μˆœμœ„λŠ” μƒλŒ€μ μΈ κ°’μœΌλ‘œ μ ˆλŒ€μ μ΄ 값이 μ•„λ‹ˆλ‹€

λ©”μ„œλ“œ

thread.setPriority(1~10);
thread.setPriority(Thread.MIN_PRIORITY); //1
thread.setPriority(Thread.NORM_PRIORITY); //5
thread.setPriority(Thread.MAX_PRIORITY);  //10

μˆœν™˜ν• λ‹Ή (Round Robin) 방식

μŠ€μΌ€μ€„λ§ λ°©μ‹μ—λŠ” μš°μ„ μˆœμœ„λ°©μ‹ 말고도 RR방식도 μžˆλŠ”λ° 이 방식은 μŠ€λ ˆλ“œλ§ˆλ‹€ μ‚¬μš©ν•  μ‹œκ°„μ„ ν• λ‹Ή(Time slice)ν•΄μ„œ 정해진 μ‹œκ°„λ§ŒνΌ λŒμ•„κ°€λ©° μ‹€ν–‰ν•˜λŠ” λ°©μ‹μœΌλ‘œ JVM에 μ˜ν•΄ κ²°μ •λ˜κΈ° λ•Œλ¬Έμ— κ°œλ°œμžκ°€ μž„μ˜λ‘œ μˆ˜μ •ν•  수 μ—†λ‹€.

Main μ“°λ ˆλ“œ

μš°λ¦¬κ°€ μžλ°”ν”„λ‘œμ νŠΈλ₯Ό 처음 μ‹œμž‘ν–ˆμ„λ•Œλ‚˜ c,c++와 같이 처음 μž‘μ„±ν•˜λŠ” main()λ©”μ„œλ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ μ‹œμž‘λ˜λŠ” μŠ€λ ˆλ“œλ‘œ mainλ©”μ„œλ“œμ˜ μ½”λ“œ 흐름이닀.

main μ“°λ ˆλ“œλ₯Ό 톡해 λ‹€λ₯Έ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜κΈ° λ•Œλ¬Έμ— λ©”μΈμŠ€λ ˆλ“œκ°€ μ—†λ‹€λ©΄ λ©€ν‹°μŠ€λ ˆλ“œλŠ” λ‹Ήμ—°νžˆ κ΅¬ν˜„ν•  수 μ—†λ‹€.

single process single thread

single process multi thread

multi process single thread

multi process multi thread

동기화

λ©€ν‹° μŠ€λ ˆλ“œλ‘œ λ™μž‘ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ—μ„œ ν•œ μŠ€λ ˆλ“œκ°€ μž‘μ—…ν•˜λŠ” 쀑에 μ‚¬μš©ν•˜λŠ” 데이터듀을 λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ κ°„μ„­ν•˜μ§€ λͺ»ν•˜λ„둝 λ§‰λŠ” 것이닀.

동기화λ₯Ό μ‹œν‚€μ§€ μ•ŠλŠ”λ‹€λ©΄ μ—¬λŸ¬κ°œμ˜ μŠ€λ ˆλ“œκ°€ λ²ˆκ°ˆμ•„κ°€λ©° λ™μž‘ν•  경우, μ–΄λ–€ μŠ€λ ˆλ“œκ°€ λ¨Όμ € 싀행될지 μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ— κ°œλ°œμžκ°€ μ˜λ„ν•˜μ§€ μ•ŠλŠ” λ™μž‘μ΄ λ°œμƒν•  수 있기 λ•Œλ¬Έμ— λ©€ν‹°μŠ€λ ˆλ“œν”„λ‘œκ·Έλž˜λ°μ—μ„œλŠ” μ€‘μš”ν•œ λ¬Έμ œμ΄λ‹€.

μ£Όμš” κ°œλ…λ“€

  • μž„κ³„ ꡬ역 (Critical Section) : μ—¬λŸ¬κ°œμ˜ μŠ€λ ˆλ“œκ°€ κ³΅μœ ν•˜λŠ” 데이터 λΈ”λŸ­μœΌλ‘œ ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œλ§Œ μ ‘κ·Όκ°€λŠ₯ν•˜λ„λ‘ μ§€μ •ν•˜λŠ” 것이 λͺ©ν‘œμ΄λ‹€.

  • μƒν˜Έ 배제 (Mutual Exclusion,Mutex) : μž„κ³„ ꡬ역을 ν—Œ μ‹œμ μ—μ„œ ν•œκ°œμ˜ μŠ€λ ˆλ“œλ§Œ μ‚¬μš©ν•  수 μžˆλ„λ‘ λ‹€λ₯Έ μŠ€λ ˆλ“œμ˜ 접근을 μ œμ–΄ν•˜λŠ” 기법

  • 동기화 : μƒν˜Έλ°°μ œμ˜ ν•œ ν˜•νƒœλ‘œ μƒν˜Έλ°°μ œλ₯Ό μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄ ν”„λ‘œμ„ΈμŠ€μ˜ 처리 μˆœμ„œλ₯Ό κ²°μ •ν•˜λŠ” 기법 (Semaphore)

  • κΈ°μ•„ μƒνƒœ (starvation) : λ‹€λ₯Έ μ“°λ ˆλ“œμ—κ²Œ μš°μ„ μˆœμœ„κ°€ λ°€λ € μžμ›μ„ 계속 할당받지 λͺ»ν•˜λŠ” μƒνƒœ

  • 곡정성(fairness) : λͺ¨λ“  μ“°λ ˆλ“œκ°€ κ³΅ν‰ν•˜κ²Œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” 것

Synchronized 이용

μž„κ³„μ˜μ—­μ„ μ„€μ •ν•˜λŠ”λ° μ‚¬μš©ν•˜λŠ” ν‚€μ›Œλ“œλ‘œ λ©”μ„œλ“œ 전체λ₯Ό μž„κ³„μ˜μ—­μœΌλ‘œ μ§€μ •ν•˜κ±°λ‚˜ νŠΉμ •ν•œ μ˜μ—­μ„ μž„κ³„μ˜μ—­μœΌλ‘œ μ§€μ •ν•˜λŠ” 방법이 μžˆλ‹€.

1. λ©”μ„œλ“œ 전체λ₯Ό μž„κ³„ μ˜μ—­μœΌλ‘œ 지정
  public synchronized void cal()  {
    ...
  }

2. νŠΉμ •ν•œ μ˜μ—­μ„ μž„κ³„ μ˜μ—­μœΌλ‘œ 지정
  synchronized(객체의 μ°Έμ‘°λ³€μˆ˜)  {
    ...
  }

μ“°λ ˆλ“œλŠ” synchronizedκ°€ 호좜된 μ‹œμ λΆ€ν„° ν•΄λ‹Ή μ½”λ“œλΈ”λŸ­μ΄ ν¬ν•¨λœ 객체의 lock을 μ–»μ–΄ μž‘μ—…μ„ μˆ˜ν–‰ν•˜λ‹€κ°€ μ’…λ£Œλ˜λ©΄ lock이 λ°˜ν™˜λ˜κ³  μ΄λŠ” λͺ¨λ‘ μžλ™μœΌλ‘œ 이루어지기 λ•Œλ¬Έμ—, κ°œλ°œμžλŠ” μž„κ³„μ˜μ—­λ§Œ μ„€μ • ν•΄μ£Όλ©΄λœλ‹€.

κ°μ²΄λŠ” lock을 ν•˜λ‚˜μ”© 가지고 있고 μ„€μ •ν•œ 객체의 lock을 κ°€μ§€κ³ μžˆλŠ” μ“°λ ˆλ“œλ§Œ μž„κ³„μ˜μ—­μ— 접근이 κ°€λŠ₯ν•˜κ³  λ‹€λ₯Έ μŠ€λ ˆλ“œλŠ” λŒ€κΈ° μƒνƒœμ— λ“€μ–΄κ°€κΈ° λ•Œλ¬Έμ— μž„κ³„μ˜μ—­μ€ μ’νžˆλŠ” 것이 μ’‹λ‹€.

λ©”μ„œλ“œμ— synchronizedλ₯Ό κ±Έλ©΄ κ·Έ λ©”μ„œλ“œκ°€ ν¬ν•¨λœ 객체(this)에 lock을 κ±°λŠ” 것과 κ°™λ‹€.

객체 없이 ν˜ΈμΆœν•˜λŠ” static λ©”μ„œλ“œμ˜ 경우 ν•΄λ‹Ή class에 lock을 κ±Έλ©΄μ„œ ν•¨μˆ˜κ°„μ— lock이 κ³΅μœ λ˜μ–΄ λ™μ‹œμ— ν˜ΈμΆœλ˜λŠ” 것을 막아 λ™κΈ°ν™”λ¬Έμ œκ°€ ν•΄κ²°λœλ‹€.

Locks

Java 5에 μΆ”κ°€λœ νŒ¨ν‚€μ§€λ‘œ java.util.concurrentκ°€ μžˆλŠ”λ° 이 νŒ¨ν‚€μ§€μ—μ„œ 동기화λ₯Ό μœ„ν•΄ λ‹€μ–‘ν•œ ν΄λž˜μŠ€λ“€μ„ μ œκ³΅ν•œλ‹€.

μ£Όμš” κΈ°λŠ₯

  • Locks : μƒν˜Έ 배제λ₯Ό μ‚¬μš©ν•  수 μžˆλŠ” 클래슀

  • Synchronizers : 동기화λ₯Ό μ²˜λ¦¬ν•˜λŠ” 클래슀 제곡 (Semaphore, CountDownLatch, CyclicBarrier, Phaser, Exchanger)

  • Atomic : 동기화가 λ˜μ–΄μžˆλŠ” λ³€μˆ˜ 제곡

  • Executors : μ“°λ ˆλ“œ ν’€ 생성, μ“°λ ˆλ“œ 생λͺ…μ£ΌκΈ° 관리와 같은 κΈ°λŠ₯ 제곡

  • Queue : Thread-safeν•œ Queue 제곡

java.util.concurrent.locks

ν•΄λ‹Ή νŒ¨ν‚€μ§€μ— μƒν˜Έ 배제λ₯Ό μœ„ν•œ Lock API듀이 정이 λ˜μ–΄μžˆκ³ , synchronized 도 lock을 μ΄μš©ν•œ λ™κΈ°ν™”μΈλ§ŒνΌ λ™μΌν•œ λ©”μ»€λ‹ˆμ¦˜μœΌλ‘œ λ™μž‘ν•œλ‹€.

λ‚΄λΆ€μ μœΌλ‘œ synchronizedλ₯Ό μ‚¬μš©ν•΄ κ΅¬ν˜„λ˜μ—ˆκ³ , λ”μš± 더 μœ μ—°ν•˜κ³  μ •κ΅ν•˜κ²Œ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ˜κ³  직접 lock을 κ±Έκ³  ν‘ΈλŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•΄μ•Ό ν•œλ‹€.

  • Interface

    • Lock : κ³΅μœ μžμ›μ— ν•œλ²ˆμ— ν•œ μ“°λ ˆλ“œλ§Œ read/writeλ₯Ό μˆ˜ν–‰ κ°€λŠ₯

    • ReadWriteLock : 곡유 μžμ›μ— μ—¬λŸ¬κ°œμ˜ μ“°λ ˆλ“œκ°€ readν•  수 μžˆμ§€λ§Œ writeλŠ” ν•œ μ“°λ ˆλ“œλ§Œ μˆ˜ν–‰ κ°€λŠ₯

    • Condition : wait()κ³Ό notify()λ₯Ό μ“°λ ˆλ“œμ˜ μ’…λ₯˜λ‘œ κ΅¬λΆ„ν•˜μ§€ μ•Šκ³ , 곡유객체의 waiting poll에 λͺ°μ•„ λ„£κ³  각 μ“°λ ˆλ“œλ₯Ό μœ„ν•œ Condition을 λ§Œλ“€μ–΄ 각각의 waiting poolμ—μ„œ λ”°λ‘œ 기닀리도둝 ν•œλ‹€.

      wait(),notify(),notifyAll() λŒ€μ‹  await(),signal(),signalAll()을 μ‚¬μš©ν•œλ‹€.

  • Interface의 κ΅¬ν˜„μ²΄

    • ReentrantLock : Lock의 κ΅¬ν˜„μ²΄λ‘œ μž„κ³„μ˜μ—­μ˜ μ‹œμž‘,쑸료 지점을 λͺ…μ‹œ ν•  수 있게 ν•΄μ€€λ‹€.

    • ReentrantReadWriteLock : ReadWriteLock의 κ΅¬ν˜„μ²΄

    • StampLock : JDK 1.8λΆ€ν„° μΆ”κ°€λ˜μ—ˆκ³ , λ‹€λ₯Έ lockκ³Ό 달리 LockμΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜μ§€ μ•Šκ³  lock을 κ±Έκ±°λ‚˜ ν•΄μ§€ν• λ•Œ μŠ€νƒ¬ν”„(longνƒ€μž… μ •μˆ˜κ°’)λ₯Ό μ‚¬μš©ν•œλ‹€.

  • μ£Όμš” λ©”μ„œλ“œ

    • lock() : Lock μΈμŠ€ν„΄μŠ€μ— lock을 건닀.

    • lockInterruptibly() : ν˜„μ œ μ“°λ ˆλ“œκ°€ interrupted μƒνƒœκ°€ μ•„λ‹λ•Œ Lock μΈμŠ€ν„΄μŠ€μ— μž κΈˆμ„ κ±Έκ³  interruptedμƒνƒœλΌλ©΄ Exception을 λ°œμƒμ‹œν‚¨λ‹€.

    • tryLock() : μ¦‰μ‹œ LockμΈμŠ€ν„΄μŠ€μ— μž κΈˆμ„ μ‹œλ„ν•˜κ³  성곡여뢀λ₯Ό boolean νƒ€μž…μœΌλ‘œ λ°˜ν™˜ν•œλ‹€.

    • tryLock(long timeout, TimeUnit timeUnit) : 잠금 μ‹€νŒ¨μ‹œ falseλ₯Ό λ°˜ν™˜ν•˜λŠ” 것이 μ•„λ‹Œ 주어진 μ‹œκ°„λ™μ•ˆ κΈ°λ‹€λ¦°λ‹€.

    • unlock() : LockμΈμŠ€ν„΄μŠ€μ˜ μž κΈˆμ„ ν•΄μ œν•œλ‹€.

    • newCondition() : ν˜„μ œ Lock μΈμŠ€ν„΄μŠ€μ™€ μ—°κ²°λœ Condition 객체λ₯Ό λ°˜ν™˜ν•œλ‹€.

Synchronized 와 Lock의 차이점

SynchronizedλŠ” μ•”μ‹œμ μΈ(implicit) lock이라고 ν•˜μ—¬ λΆˆκ³΅μ • λ¬Έμ œκ°€ λ°œμƒ ν•  수 μžˆμ–΄ μ“°λ ˆλ“œ μˆœμ„œλ₯Ό 보μž₯ν•˜μ§€ μ•Šμ§€λ§Œ Lock은 μˆœμ„œλ₯Ό 지정할 수 μžˆμ–΄ λͺ…μ‹œμ μΈ(explict) lock이라고 ν•œλ‹€.

SynchronizedλŠ” lock을 μžλ™ νšλ“/λ°˜ν™˜ν•΄μ£ΌκΈ° λ•Œλ¬Έμ— μ½”λ“œκ°€ κ°„κ²°ν•˜λ‹€λŠ” μž₯점이 있고, Lock은 ReentrantLock을 μ΄μš©ν•˜λ©΄ μƒμ„±μž 인자λ₯Ό 톡해 곡정/λΆˆκ³΅μ •μ„ μ„€μ • ν•  수 μžˆμ–΄ κ³΅μ •ν•œ lock을 μ‚¬μš©ν•  경우 μžμ› 할당을 μœ„ν•΄ 경쟁이 λ°œμƒν–ˆμ„ λ•Œ 였래 κΈ°λ‹€λ¦° μŠ€λ ˆλ“œμ—κ²Œ lock을 μ œκ³΅ν•˜μ—¬ κΈ°μ•„ 문제λ₯Ό ν•΄κ²°ν•  수 μžˆλ‹€.

volatile

λ©€ν‹° μ½”μ–΄ ν”„λ‘œμ„Έμ„œλŠ” μ½”μ–΄λ§ˆλ‹€ λ³„λ„μ˜ μΊμ‹œλ₯Ό κ°–κ³  μžˆλŠ”λ° λ³€μˆ˜ μ•žμ— volatile ν‚€μ›Œλ“œλ₯Ό 뢙이면 μ½”μ–΄κ°€ λ³€μˆ˜μ˜ 값을 μ½μ–΄μ˜¬ λ•Œ μΊμ‹œκ°€ μ•„λ‹Œ λ©”λͺ¨λ¦¬μ—μ„œ μ½μ–΄μ˜€κΈ° λ•Œλ¬Έμ— μΊμ‹œμ™€ λ©”λͺ¨λ¦¬κ°„μ˜ 뢈일치λ₯Ό ν•΄κ²°ν•  수 μžˆλ‹€.

JVM은 데이터λ₯Ό 4byte λ‹¨μœ„λ‘œ μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— intλ‚˜ 이보닀 μž‘μ€ νƒ€μž…μ˜ λ°μ΄ν„°λŠ” ν•œλ²ˆμ— μ½κ±°λ‚˜ μ“°λŠ” 것이 κ°€λŠ₯ν•˜λ‚˜ 그보닀 큰 데이터 νƒ€μž…μ΄λΌλ©΄ ν•˜λ‚˜μ˜ λͺ…λ Ήμ–΄λ‘œ 값을 μ½κ±°λ‚˜ μ“Έ 수 μ—†λ‹€. λ•Œλ¬Έμ— λ³€μˆ˜μ˜ 값을 μ½λŠ” κ³Όμ •μ—μ„œ λ‹€λ₯Έ μ“°λ ˆλ“œκ°€ 끼어듀 ν‹ˆμ΄ 있기 λ•Œλ¬Έμ— λ³€μˆ˜λ₯Ό 읽고 μ“°λŠ” λ¬Έμž₯을 synchronizedλΈ”λŸ­μœΌλ‘œ κ°μ‹Έκ±°λ‚˜ λ³€μˆ˜λ₯Ό volatile을 λΆ™μ—¬ μ„ μ–Έν•˜λŠ” λ°©λ²•μœΌλ‘œ μΊμ‹œμ™€ λ©”λͺ¨λ¦¬κ°„ 뢈일치 문제λ₯Ό ν•΄κ²° ν•  수 μžˆλ‹€.

μƒμˆ˜μ—λŠ” volatileλ₯Ό 뢙일 수 μ—†μ–΄ λ³€μˆ˜μ— final을 같이 μ“Έ 수 μ—†λ‹€. (μƒμˆ˜λŠ” λ³€ν•˜μ§€ μ•ŠλŠ” 값이기 λ•Œλ¬Έμ— thread-safeν•˜κΈ° λ•Œλ¬Έμ—)

λ°λ“œλ½

κ΅μ°©μƒνƒœλΌκ³ λ„ ν•˜λ©° μƒν˜Έ λ°°μ œμ— μ˜ν•΄ λ‚˜νƒ€λ‚˜λŠ” 문제둜, λ‘˜ μ΄μƒμ˜ μŠ€λ ˆλ“œκ°€ 데이터λ₯Ό μ μœ ν•œ μƒνƒœμ—μ„œ μ„œλ‘œ λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ μ μœ ν•˜κ³  μžˆλŠ” 데이터λ₯Ό μš”κ΅¬ν•˜κ²Œλ˜μ–΄ 끝없이 κΈ°λ‹€λ¦¬λŠ” μƒνƒœ

λ°μ΄ν„°λ² μ΄μŠ€ νŠΈλžœμ μ…˜μ—μ„œλ„ λ°œμƒν•˜λŠ”λ° ν•œ νŠΈλžœμž­μ…˜μ—μ„œ ν•œ λ ˆμ½”λ“œμ— μ—…λ°μ΄νŠΈλ₯Ό μˆ˜ν–‰ν•  λ•Œ, 이 λ ˆμ½”λ“œλŠ” μ—…λ°μ΄νŠΈλ₯Ό μœ„ν•΄ λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ˜ 접근을 막도둝 lock을 κ±°λŠ”λ° λ‹€μˆ˜μ˜ νŠΈλžœμ μ…˜μ΄ λ™μ‹œμ— λ ˆμ½”λ“œλ₯Ό μ—…λ°μ΄νŠΈν•˜λ €ν•œλ‹€λ©΄ λ°λ“œλ½μ— 빠질 μœ„ν—˜μ΄ μžˆλ‹€.

λ°λ“œλ½ λ°œμƒ 쑰건(이유)

  1. Mutual Exclusion(Mutex) : μžμ›μ— λ™μ‹œ μ ‘κ·Ό λΆˆκ°€λŠ₯

  2. Hold & Wait : μ μœ ν•˜κ³  기닀리기 (μžμ›μ„ μ μœ ν•œμ±„λ‘œ λ‹€λ₯Έ μŠ€λ ˆλ“œμ˜ μžμ›μ„ λ°˜λ‚©λ°›κΈΈ κΈ°λ‹€λ¦¬λŠ” 것)

  3. No Preemption : μžμ›μ„ κ°•μ œλ‘œ λΊμ–΄μ˜€μ§€ λͺ»ν•¨

  4. Circular Wait : μˆœν™˜ ν˜•νƒœλ‘œ λŒ€κΈ°

λ°λ“œλ½ ν•΄κ²° 방법

  1. λ°λ“œλ½ 예방 : λ°λ“œλ½ λ°œμƒ 쑰건 4가지λ₯Ό μ›μ²œ λ΄‰μ‡„ν•˜λŠ” 방법

  2. λ°λ“œλ½μ˜ νšŒν”Ό : μžμ›μ΄ μ–΄λ–»κ²Œ μš”μ²­λ μ§€μ— λŒ€ν•œ 좔가정보λ₯Ό μ œκ³΅ν•˜λ„λ‘ μš”κ΅¬ν•˜λŠ” κ²ƒμœΌλ‘œ μžμ› ν• λ‹Ήμƒνƒœλ₯Ό κ²€μ‚¬ν•˜λŠ” 방법

  3. λ°λ“œλ½ 탐지 & 회볡 : λ°λ“œλ½μ΄ λ°œμƒν–ˆμ„λ•Œ ν•΄κ²°ν•˜λŠ” 방법

  4. λ°λ“œλ½ λ¬΄μ‹œ : μ•„μ£Ό 적은 ν™•λ₯ λ‘œ λ°λ“œλ½μ΄ λ°œμƒν•œλ‹€λ©΄, λ¬΄μ‹œν•˜λŠ” 방법

λ¬΄μ‹œν•˜κ±°λ‚˜ μž¬μ‹€ν–‰ν•˜λŠ” 편이 μžμ›μ μœΌλ‘œ 더 이득을 λ³Ό μˆ˜λ„ 있기 λ•Œλ¬Έμ— μˆ˜ν–‰ν•˜λŠ” 방법

λ°λ“œλ½ 방지 방법듀

  1. 락 μ •λ ¬ : λͺ¨λ“  락이 항상 같은 μˆœμ„œλ‘œ νšλ“λœλ‹€λ©΄ λ°λ“œλ½μ€ λ°œμƒν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— νš¨κ³Όμ μ΄λ‚˜ 락의 μˆœμ„œλ₯Ό μ•Œκ³  μžˆλŠ” μƒνƒœμ—μ„œλ§Œ μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€. (λ°λ“œλ½ 예방)

  2. 락 νƒ€μž„ 아웃 : 락을 νšλ“ν•˜κΈ° μœ„ν•΄ κΈ°λ‹€λ¦¬λŠ” μ‹œκ°„μ„ 정해놓고 μ‹œκ°„μ΄ μ§€λ‚œ ν›„μ—λŠ” 락을 λ‹€μ‹œ μ‹œλ„ν•˜λŠ” 방법 (λ°λ“œλ½ 탐지&회볡)

같은 락을 νšλ“ν•˜λ €λŠ” λ‹€λ₯Έ λͺ¨λ“  μ“°λ ˆλ“œμ—κ²Œ 기회λ₯Ό μ£ΌλŠ” 것이기 λ•Œλ¬Έμ— 곡정성 λ¬Έμ œκ°€ λ°œμƒν•˜κ²Œλ˜κ³ , λ°λ“œλ½ 뿐만이 μ•„λ‹Œ μž‘μ—…μ„ μ²˜λ¦¬μ€‘μ—λ„ νƒ€μž„μ•„μ›ƒμ΄ λ°œμƒν•  수 도 μžˆλ‹€.

  1. λ°λ“œλ½ 감지 : 자료ꡬ쑰λ₯Ό μ΄μš©ν•˜μ—¬ μ“°λ ˆλ“œκ°€ 락을 νšλ“ν•˜λ©΄ μ €μž₯ν•˜κ³  이λ₯Ό μ΄μš©ν•΄ λ°λ“œλ½μ„ κ°μ§€ν•˜κ³  λ°λ“œλ½μ΄ λ°œμƒν–ˆλ‹€λ©΄, 락을 ν•΄μ œν•˜λŠ” 방법

  2. Atomic Variable : volatile ν‚€μ›Œλ“œλ‚˜ java.util.concurrent.atomic 클래슀λ₯Ό μ΄μš©ν•˜μ—¬ μ›μž λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” 방법

μ“°λ ˆλ“œ ν’€

μ“°λ ˆλ“œλ₯Ό 미리 λ§Œλ“€μ–΄ ν•„μš”ν• λ•Œ μ‚¬μš©ν•˜κ³  λ°˜λ‚©ν•  수 μžˆλ„λ‘ ν•˜λŠ” ν’€μž₯의 κ°œλ…

java.util.concurrent.Executorsμ—μ„œ μŠ€λ ˆλ“œν’€ κ΄€λ ¨ APIλ₯Ό μ œκ³΅ν•œλ‹€.

μž₯점

  • μŠ€λ ˆλ“œλ₯Ό 생성/μˆ˜κ±°ν•˜λŠ”λ° λΉ„μš©μ΄ 적닀.

  • μœ„μ˜ 이유둜 λ‹€μˆ˜μ˜ μ‚¬μš©μž μš”μ²­μ„ μ²˜λ¦¬ν• λ•Œ λΉ λ₯΄κ²Œ λŒ€μ‘ κ°€λŠ₯ν•˜λ‹€.

단점

  • μ‚¬μš©ν•˜λŠ” 양보닀 많이 생성해두면 λ©”λͺ¨λ¦¬ λ‚­λΉ„κ°€ λœλ‹€.

  • λ…ΈλŠ” μŠ€λ ˆλ“œκ°€ λ°œμƒν•  수 μžˆλ‹€.

μ“°λ ˆλ“œ ν’€μ˜ ꡬ쑰

  1. Task의 μš”μ²­μ„ λ°›λŠ” Queue

  2. μ‹€μ œ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•˜λŠ” manager

  3. Queue μ•ˆμ—μ„œ Taskλ₯Ό 꺼내와 μ‹€μ œ μ“°λ ˆλ“œμœ„μ— λ™μž‘μ‹œμΌœ κ²°κ³Όλ₯Ό μ–»λŠ” λΆ€λΆ„

생성 λ©”μ„œλ“œ

  1. Executors.newFixedThreadPool(10) : μ΅œλŒ€ μ“°λ ˆλ“œλ₯Ό 10κ°œκΉŒμ§€ λ§Œλ“œλŠ” ν’€

  2. Executors.newCachedThreadPool() : μ“°λ ˆλ“œ 수의 μ œν•œμ„ 두지 μ•ŠλŠ” ν’€λ‘œ μ“°λ ˆλ“œ μ‹œμž‘ μš”μ²­μ΄ λ“€μ–΄μ˜¬λ•Œ μƒμ„±ν•˜κ³  μ’…λ£Œμ‹œμ— λ°”λ‘œ μ£½μ§€μ•Šκ³  짧은 μ‹œκ°„(1λΆ„)λ™μ•ˆ μ‚΄μ•„μžˆλŠ” λ°©λ²•μ˜ ν’€

  3. Executors.newSingleThreadExecutor() : ν•˜λ‚˜μ˜ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜λŠ” ν’€λ‘œ μ“°λ ˆλ“œ μž‘μ—…μ€‘μ— μ˜ˆμ™Έμƒν™©μ΄ λ°œμƒν•œ 경우 μ˜ˆμ™Έμ²˜λ¦¬λ₯Ό μœ„ν•œ μ“°λ ˆλ“œμš©μœΌλ‘œ 많이 μ‚¬μš©ν•œλ‹€.

  4. Executors.newScheduledThreadPool() : 일정 μ£ΌκΈ°λ§ˆλ‹€ λ°˜λ³΅ν•΄μ•Όν•˜λŠ” μŠ€λ ˆλ“œλ₯Ό μ—¬λŸ¬κ°œ μƒμ„±ν•˜κΈ° μœ„ν•œ ν’€

적정 μŠ€λ ˆλ“œν’€μ˜ μ‚¬μ΄μ¦ˆλŠ” μ–΄λ–»κ²Œ μ•ŒκΉŒ?


Reference

https://nesoy.github.io/articles/2018-09/OS-Concurrency-Parallelism

https://ryan-han.com/post/java/java-thread/

https://zbomoon.tistory.com/12

https://widevery.tistory.com/28

https://goscala.tistory.com/171

https://tourspace.tistory.com/54

Last updated