본문 바로가기

# 02/Java

[윤성우 열혈자바] 34-2. 쓰레드의 동기화

반응형

쓰레드의 메모리 접근 방식과 그에 따른 문제점


class Counter {

   int count = 0;  // 공유되는 변수

   

   public void increment() {

      count++;  //  번째 쓰레드에 의해 실행

   }

   public void decrement() {

      count--;  //  다른 쓰레드에 의해 실행

   }

   public int getCount() { return count; }

}



class MutualAccess {

   public static Counter cnt = new Counter();

   

   public static void main(String[] args) throws InterruptedException {

      Runnable task1 = () -> {

         for(int i = 0; i < 1000; i++)

            cnt.increment(); // 값을 1 증가

      };

   

      Runnable task2 = () -> {

         for(int i = 0; i < 1000; i++)

            cnt.decrement(); // 값을 1 감소

      };

      Thread t1 = new Thread(task1);

      Thread t2 = new Thread(task2);

      t1.start();

      t2.start();

      t1.join();           // t1 참조하는 쓰레드의 종료를 기다림

      t2.join();           // t2 참조하는 쓰레드의 종료를 기다림

      System.out.println(cnt.getCount());

   }

}



일반적으로 1000번 증가 1000번 감소해서 0이 나와야 되는데 0이 나올 확률이 적음..








쓰레드1이 변수 num을 1 증가 시킨다고 하면 변수 num의 값을 가지고 와서 증가시키고 다시 갖다놓는 3단계를 거쳐야 한다.

쓰레드가 동시에 일어날때는 3단계 중간에 교체가 될 수 도 있어 갖다놓는 과정에서 다른 쓰레드가 계산한 값을 무시하고 갖다 놓을 수 있어 생각했던 값이 아닌 다른 값이 계산 될 수 있다.

특정 변수를 두 개 이상의 쓰레드가 동시 접근하면 이런 현상이 발생할 수 있다.








동기화(Synchronization) 메소드 - 특정 메소드를 둘 이상의 쓰레드가 접근하지 못하게 하는 것!


class Counter {

   int count = 0;

 

   synchronized public void increment() {

      count++;

   }

 

   synchronized public void decrement() {

      count--;

   }

 

   public int getCount() { return count; }

}


각각 다른 메소드라도 한 클래스 내에 있으면 동시에 둘 이상의 쓰레드가 접근하지 못하게 할 수 있다!!


단, 각각의 메소드가 기능이 엄청 많은데 단 한줄만 같은 변수를 접근하여 동기화를 해줘야 한다고 할때는 비효율 적이다!!








동기화(Synchronization) 블록

 - 특정 메소드 중 특정 기능만이 둘 이상의 쓰레드가 접근하지 못하게 하는 것!


class Counter {

   int count = 0;

 

   public void increment() {

      synchronized(this) {    // 동기화 블록 - this라는 인스턴스를 대상으로 동기화 하겠다는 뜻.

         count++;

      }

   }

 

   public void decrement() {

      synchronized(this) {    // 동기화 블록

         count--;

      }

   }

 

   public int getCount() { return count; }

}


메소드 보다 더 작은 단위로 동기화 시킬 수 있다. 해당 메소드의 특정 기능만이 동시에 두 개 쓰레드가 접근하지 못하도록 하는 방법이다!


이 예제에서는 동기화 할 경우가 한 가지밖에 없어서 this를 사용했지만, 만약 변수가 2개 이상존재하고 각각의 변수에 대해 동기화 해야되는 블록이 존재한다면 this 대신 a1, a2 이런식으로 구분을 지어줄 수 있다.



반응형