포스트

Thread synchronization

Thread synchronization

Thread - synchronization(23. 10. 18)

Thread 멈추기

  • Thread()run() 에 의해 시작되지만 종료 명령어는 없다.
  • 과거에는 stop() 이 있었지만 데드락, 내부 리소스 정리 등 문제로 삭제
  • Thread의 안전한 종료를 위해 안전하게 스스로 종료할 수 있게 구현

상태 제어 이용하기

  • Thread 안에서 내부 상태 제어가 가능하다
  • 안전하게 종료하는법
    • flag 변수로 제어하기

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
        boolean runnuingflag = true;
              
            RunableCounter(String str, int counter) {
                this.str = str;
                this.maxCount = counter;
            }
              
            @Override
            public void run() {
                while (runnuingflag) {
                    try {
                        if (counter == 59)
        						...
        				}
        	}
      
    • interrupt() 를 활용

      1
      2
      3
      4
      5
      6
      7
      
        public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    try {      
                        System.out.println(str + " : " + counter++);
        								...
        			}
        }
      

Thread 동시성 제어

  • 하나의 Process내에서 자원을 공유하는 여러 스레드의 문제점은 어떤게 있을까
  • Race Condition
    • 둘 이상의 thread가 동시에 공유 자원에 접근 할 시 발생
    • [SharedCounter.java](http://SharedCounter.java)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
        public class SharedCounter extends Thread {
            SharedCount sharedCount;
            int count;
            int maxCount;
              
            public SharedCounter(String name, int maxCount, SharedCount sharedCount) {
                setName(name);
                this.sharedCount = sharedCount;
                this.maxCount = maxCount;
                count = 0;
            }
              
            @Override
            public void run() {
                while (count < maxCount) {
                    count++;
                    sharedCount.increment();
                }
            }
        }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
        public class SharedCount {
            int count;
              
            public int getCount() {
                return count;
            }
              
            public void setCount(int count) {
                this.count = count;
            }
              
            public void increment() {
                setCount(getCount() + 1);
            }
        }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
        SharedCount sharedCount = new SharedCount();
                SharedCounter counter1 = new SharedCounter("counter1", 10000, sharedCount);
                SharedCounter counter2 = new SharedCounter("counter2", 10000, sharedCount);
              
                counter1.start();
                counter2.start();
              
        sharedCount : 14047
        ...
        sharedCount : 14375
      
    • 결과는 20000을 예상하지만 실행할때마다 다른 결과가 나타난다.
    • counter1 이 1을 추가하고 메모리에 적었지만 counter2 가 곧이어 다시 써버려서 counter1이 덮어진다
    • 이러한 구역을 임계 구역 이라고 부른다.
    • 이러한 문제를 해결하기 위해 상호 배제 를 사용 → 공유 자원의 접근을 나의 process나 thread로 제한
  • Deadlock
    • 공유 자원의 접근 권한 등으로 통제하던 도중 반환 관계가 꼬임으로써 발생
    • Hold and Wait
      • P1이 R1 을 기다리고 있는 도중 P2가 R1를 실행하던 도중 R2도 사용할 필요가 있음 하지만 P3이 R3을 사용하고 있을 경우… R1은 언제 P1을 사용할 수 있을까…
    • Circular Wait
      • 위의 Hold and Wait가 순환 형태를 이루고 있는 경우
      • 식사하는 철학자가 대표적인 문제
    • Starvation
      • 다른 process나 thread가 지속적으로 한 resource를 가짐으로써 다른 thread가 계속해서 접근 권한을 얻지 못할 경우에 발생 → 무한한 대기 상태
    • Licelock
      • 접근이 없는 권한의 경우 해당 자원 대신에 다른 자원을 수행하고 있는 것
      • 해당 작업만 처리되지 못하고 나머지 작업은 처리 되는 것의 차이를 가지고 있다.

Synchronized

Java에서 thread 동기화를 위해 synchronized 적용 가능한 곳

  • Instance methods
  • Static methods
  • Code blocks

위의 SharedCount.java에 Synchronized 키워드를 적용

1
2
3
4
5
synchronized public void increment() {
        setCount(getCount() + 1);
    }

sharedCount : 20000
  • 정상적으로 sharedCount가 나타남을 볼 수 있다.
  • static 을 사용해서 싱글톤 패턴 흉내

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      public class SharedCount {
          static int count;
        
          public static int getCount() {
              return count;
          }
        
          public static void setCount(int count) {
              SharedCount.count = count;
          }
        
          static synchronized public void increment() {
              setCount(getCount() + 1);
          }
      }
    

    thread safety가 더 강화됨을 확인할 수 있다.

Synchronization Control

  • 이러한 병행성의 제어를 위해 waitnotify 지원
  • wait()
    • 어떠한 thread가 자신의 제어권을 양보하고 WATING 상태에서 대기하는 명령어
    • notify() 를 호출함으로써 RUNNABLE 상태로 변경
  • notify()
    • WAITING 상태에 있는 영역을 RUNNABLE로 변경 하는 역할

멀티 스레드 환경에서는

  • Java는 multi-thread 언어이므로, 이러한 동기화는 상호배제를 달성하는 좋은 방법이다.
  • 변수에는 synchronized 를 적용할 수 없다.
  • synchronized block의 object가 null인 경우는 null point exception을 발생
  • Java 동기화는 동시 읽기를 허용하지 않는다.
  • synchronized method는 매우 느리다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.