배운것들을 정리합니다.
三昧境

Java/Java Language

[Java 기초] 쓰레드 Thread, Runnable, Join, MultiThread, Synchronized

ujo_orr 2024. 11. 29. 00:47

쓰레드(Thread)

여러작업을 동시에 수행할 때

/*
class 클래스명 extends Thread { // Thread 클래스 상속
  public void run() { // public void run() {}메소드 정의
    
  }
}
*/

class MyThread extends Thread {
  public void run() {
    for (int i = 1, i <= 5; i++) {
      System.out.println("Thread : " + i);
    }
  }
}

public class Main {
  public static void main(String[] args) {
    MyThread thread = new MyThread();
    thread.start(); // 새로운 쓰레드에서 run() 동작 수행
  }
}

thread.start() 를 호출한 쓰레드는 다음 코드를 실행하러 이동하며
새로운 쓰레드는 run()메서드의 내용을 실행하는 병렬적 진행임


Runnable

여러 작업을 동시에 수행할 때

/*
class 클래스명 implements Runnable { // 인터페이스 이기에 다른 클래스 상속 가능
  public void run() {
  
  }
}
*/

class MyRunnable implements Runnable {
  public void run() {
    for(int i = 1; i <= 5; i++) {
      System.out.println("Runnable : " + i);
    }
  }
}

public class Main {
  public static void main(String[] args) {
    MyRunnable runnable = new MyRunnable();
    Thread thread = new Thread(runnable);
    thread.start();
  }
}
특징 Thread Runnable
구현 방식 Thread Class를 extends Runnable Class를 implements
확장성 다른 클래스를 상속받을 수 없음 다른 클래스를 상속받을 수 있음
사용 목적 Thread의 실행 로직과 Thread 객체를 하나로 결합 실행 로직과 Thread 객체를 분리
실행 방법 직접 start() 호출 Thread 객체를 생성하여 실행
객체 구조 쓰레드와 작업 로직이 한 클래스에 존재 쓰레드 로직과 객체가 독립적으로 존재
사용처 Thread로직과 Thread 객체가 하나로 결합되어도 문제 없을 때
추가적인 상속이 필요 없을 대
다른 클래스를 상속받아야 하는 경우
Thread 풀을 사용할 때
좀 더 자주 쓰임

Join

쓰레드의 실행을 마칠때까지 대기하기

public class Main {
  public static void main(String[] args) {
    Thread thread = new Thread(() -> {
      for(int i = 1, i <= 5; i++) {
        System.out.println("Thread : " + i);
      }
    });
    thread.start();
    method();
  }
}

public static void method() {
  for(int i = 1, i <= 5, i++) {
    System.out.println("Method : " + i);
  }
}

// thread1, method1, thread2, method2 번갈아서 출력

public class Main {
  public static void main(String[] args) throws InterruptedException { // join()을 사용하는 도중 예외가 발생할 수 있기에
    Thread thread = new Thread(() -> {
      for(int i = 1, i <= 5; i++) {
        System.out.println("Thread : " + i);
      }
    });
    thread.start();
    thread.join(); // Dart에 async await 느낌 // thread의 실행을 마칠때까지 대기
    method();
  }
}

public static void method() {
  for(int i = 1, i <= 5, i++) {
    System.out.println("Method : " + i);
  }
}

// thread1, thread2, ... 순차적 실행

다중 쓰레드(Multi Thread)

여러 쓰레드를 동시에 수행

public class Main {
  public static void main(String[] args) {
    Thread thread = new Thread(() -> {
      for(int i = 1, i <= 5; i++) {
        System.out.println("Thread : " + i);
      }
    });
    Thread thread2 = new Thread(() -> {
      for(int i = 1, i <= 5; i++) {
        System.out.println("Thread2 : " + i);
      }
    });
    thread.start();
    thread2.start();
  }
}
// thread: 1, thread2: 1, thread:2, thread2: 2, ... 병렬적 실행
// 수행하는 PC마다 수행결과가 달라질 순 있음

동기화(Synchronization)

여러 쓰레드가 공유된 자원에 동시에 접근하지 못하게 막는것

/*
synchronized 메소드명() {
  // 특정 스레드가 이 메소드를 수행하는 동안엔 다른 스레드는 이 메소드를 수행할 수 없게됨
}

synchronized(변수) {
  // 특정 스레드가 이 메소드를 수행하는 동안엔 다른 스레드는 이 변수의 값을 바꿀 수 없게 됨
}
*/

class SharedData {
  public int data = 0;
  synchronized public void increment() {
    data++;
  }
}

public class Main {
  public static void main(String[] args) throws InterruptedException { // join() 메소드 예외처리
    SharedData sharedData = new SharedData();
    Thread thread1 = new Thread(() -> {
      for(int = i; i < 1000; i++) {
        sharedData.increment();
      }
    });
    Thread thread2 = new Thread(() -> {
      for(int = i; i < 1000; i++) {
        sharedData.increment();
      }
    });
    thread1.start();
    thread2.start();
    thread1.join();
    thread2.join();
    System.out.println("SharedData : " + shareData.data); // SharedData : 2000 출력
  }
}

// 만약 여기서 synchronized를 삭제하고 코드를 실행한다면

class SharedData {
  public int data = 0;
  public void increment() { // synchronized 삭제
    data++;
  }
}

public class Main {
  public static void main(String[] args) throws InterruptedException { // join() 메소드 예외처리
    SharedData sharedData = new SharedData();
    Thread thread1 = new Thread(() -> {
      for(int = i; i < 1000; i++) {
        sharedData.increment();
      }
    });
    Thread thread2 = new Thread(() -> {
      for(int = i; i < 1000; i++) {
        sharedData.increment();
      }
    });
    thread1.start();
    thread2.start();
    thread1.join();
    thread2.join();
    System.out.println("SharedData : " + shareData.data); // SharedData : 1768 출력 // 원치 않는 값이 나올 수 있음
  }
}