개발 무지렁이

[Java] 공유객체 내부데이터에 대한 스레드 동기화와 정확한 작업교대 본문

Backend/자바

[Java] 공유객체 내부데이터에 대한 스레드 동기화와 정확한 작업교대

Gaejirang-e 2023. 8. 13. 18:53

𐁍 스레드 동기화
멀티스레드는 공유객체에 서로 접근할 수 있다.
다른 스레드에 의해 공유객체내부데이터가 쉽게 변경되기 때문에,
하나의 스레드가 접근할 때, 다른 스레드가 접근하지 못하도록
작업이 끝날 때까지 객체에 잠금(Lock🔒)을 걸어야 한다.
(그렇지 않으면 의도치 않은 결과가 나올 수 있다.)
이를 위해, 자바는 동기화 메서드동기화 블록을 제공한다.

🦔 동기화 메서드 및 동기화 블록
⚠️ 공유객체 내부에 동기화 메서드 및 동기화 블록을 정의해 놓는다.
🕹️ 동기화 메서드
  (동기화 메서드를 실행하는 즉시, 🔒객체잠금이 일어나고, 실행이 끝나면 잠금이 풀린다.)
public synchronized void method() {
    //하나의 스레드만 실행할 수 있는 영역
}

🍫 동기화 블록
  (일부 코드영역을 실행할 때만 🔒객체잠금을 걸고 싶다면, 동기화 블록을 만들면 된다.)

synchronized([공유객체]) {
    //하나의 스레드만 실행할 수 있는 영역
}

📜 Calculator.java

  //🎃 공유객체
  public class Calculator {
      private int memory;

      public int getMemory() {
          return memory;
      }

        //🕹️ 동기화 메서드
      public synchronized void setMemory1(int memory) {
          this.memory = memory;
          try {
              Thread.sleep(2000);
          } catch (InterruptedException e) { }
          System.out.println(Thread.currentThread().getName() + ": " + this.memory);
      }

      public void setMemory2(int memory) {
            //🍫 동기화 블록
          synchronized(this) {
              this.memory = memory;
              try {
                  Thread.sleep(2000);    

              } catch (InterruptedException e) { }
              System.out.println(Thread.currentThread().getName() + ": " + this.memory);
          }
      }
  }

📜 User1Thread.java

  //🪡 스레드1
  public class User1Thread extends Thread {
      private Calculator calculator;

      public User1Thread() {
          setName("User1Thread");
      }

      //🎃 공유객체 초기화
      public void setCalculator(Calculator calculator) {
          this.calculator = calculator;
      }

      @Override
      public void run() {
          calculator.setMemory1(100);
      }
  }

📜 User2Thread.java

  //🪡 스레드2
  public class User2Thread extends Thread {
      private Calculator calculator;

      public User2Thread() {
          setName("User2Thread");
      }

        //🎃 공유객체 초기화
      public void setCalculator(Calculator calculator) {
          this.calculator = calculator;
      }

      @Override
      public void run() {
          calculator.setMemory2(50);
      }
  }

📜 SynchronizedExample.java

  public class SynchronizedExample {
      public static void main(String[] args) {
            //🎃 공유객체 생성
          Calculator calculator = new Calculator();

          //🪡 스레드1 생성, 공유객체 지정, 작업 실행
          User1Thread user1Thread = new User1Thread();
          user1Thread.setCalculator(calculator);
          user1Thread.start();

          //🪡 스레드2 생성, 공유객체 지정, 작업 실행
          User2Thread user2Thread = new User2Thread();
          user2Thread.setCalculator(calculator);
          user2Thread.start();

          //User1Thread: 100
          //User2Thread: 50
      }
  }
⭐ 스레드 동기화 원리
user1Thread.start()를 통해 run() 메서드를 실행하고,
동기화 메서드 setMemory1()을 실행하는 순간 Calculator 객체를 잠근다.🔒

𖠃 정확한 교대작업 (두개의 스레드를 교대로 번갈아가며 실행)
⚠️ 공유객체는 두 스레드가 작업할 내용을 각각 동기화 메서드로 정의해 놓는다.
⚠️ wait()와 notify(), notifyAll()동기화 메서드 or 동기화 블록 내에서만 사용할 수 있다.

📜 WorkObject.java

  // 🎃 공유객체
  public class WorkObject {
    //🕹️ 동기화 메서드
      public synchronized void methodA() {
          Thread thread = Thread.currentThread();
          System.out.println(thread.getName() + ": methodA 작업 실행");
          notify(); //다른 스레드를 SUSPENDED -> RUNNABLE
          try {
              wait(); //자신 스레드를 SUSPENDED
          } catch(InterruptedException e) { }
      }
    //🕹️ 동기화 메서드
      public synchronized void methodB() {
          Thread thread = Thread.currentThread();
          System.out.println(thread.getName() + ": methodB 작업 실행");
          notify();
          try {
              wait();
          } catch(InterruptedException e) { }
      }
  }

📜 ThreadA.java

  //🪡 스레드A
  public class ThreadA extends Thread {
      private WorkObject workObject;

      public ThreadA(WorkObject workObject) {
          setName("ThreadA");
          this.workObject = workObject;
      }

      @Override
      public void run() {
          for(int i = 0; i < 10; i++) {
            workObject.methodA();                      
        }
      }
  }

📜 ThreadB.java

  //🪡 스레드B
  public class ThreadB extends Thread {
      private WorkObject workObject;

      public ThreadB(WorkObject workObject) {
          setName("ThreadB");
          this.workObject = workObject;
      }

      @Override
      public void run() {
          for(int i = 0; i < 10; i++) {
            workObject.methodB();                      
        }
      }
  }

📜 WaitNotifyExample.java

  public class WaitNotifyExample {
      public static void main(String[] args) {
        //🎃 공유객체 생성
        WorkObject workObject = new WorkObject();

        //🪡 스레드 A,B 생성
        ThreadA threadA = new ThreadA();
        ThreadB threadB = new ThreadB();

        //🪡 각각의 스레드 작업 실행
        threadA.start();
        threadB.start();

        //ThreadA: methodA 작업 실행
        //ThreadB: methodB 작업 실행
        //ThreadA: methodA 작업 실행
        //ThreadB: methodB 작업 실행
        //...
    }
  }

📕 참고 자료 📕
Tistory's Card

Comments