Thread가 여러 개가 각각 완벽하게 독립적인 일을 수행할 수도 있지만, 때로는

그렇지 않고 하나의 객체를 경쟁하면서 사용할 수도 있고 혹은 Thread끼리 정보를 주고

받는 등의 협력을 할 수도 있습니다. 

만약 Thread가 서로 완벽하게 독립적이지 않고 하나의 객체를 서로 경쟁하면서 사용하려고

할 때가 많은데 이때는 반드시 주의해야 합니다. 문제가 아주 복잡해질 수 있으니까요.

 

예를 하나 들어보겠습니다.

짝수를 항상 돌려주는 객체를 생각해보겠습니다.

 

Even객체를 만들고 next()메소드를 호출하면 항상 짝수를 돌려주겠지요. 의심의 여지가

없는 완벽한 클래스입니다. (숫자가 증가해서 Overflow나는것은 생각하지 말구요.)

 

/**

* 항상 짝수만 만드는 클래스입니다.

*/

public class Even {

    private long number = 0;

 

    public long next() {

        number++;

        number++;

        return number;

    }

}

예제 8 - 2 Even.java

 

 그러나 문제가 없다는 주장하는 것은 하나의 Thread Even객체를 이용하는 경우일 때의

얘기입니다. 만약 여러 개의 Thread Even객체의 레퍼런스를 가지고 동시에 메소드를

호출 했을때는 미묘한 문제가 발생할 수 있습니다.

무슨 말인가 하면, Thread Even객체의 레퍼런스를 가지고 동시에 public int next()

호출하면 number++가 두번 호출되고 그 값이 리턴됩니다.

여기서 꼼꼼히 살펴보겠습니다. number++ number = number + 1의 의미입니다.

이는 number변수의 값을 읽어서 그 값에 1을 더하란 말인데 경쟁하는 Thread가 있어서

Even객체의 레퍼런스를 가지고 public int next()를 호출했다면 number값에 변화가 생길 수

있습니다.

 

Thread A

Thread B

read 0

 

write 1 (1증가 시킴)

 

 

read 1

 

write 2 (1 증가시킴)

read 2

read 2

 

write 3 (1 증가시킴)

write 3 (1증가 시킴)

return 3

return 3

 

8 - 1 경쟁하는 Thread Even객체의 next()메소드를 호출했을 때

 

8 - 1 Even객체 하나를 두고 경재하는 Thread A Thread B가 시간순서에 따라

공유하는 Even객체의 next()메소드를 호출한 후의 메모리 상태를 언급한 것입니다.

Thread A의 경우 number값을 읽으면 0 이어서 1로 증가했구요. 다음에 읽었을때는

1이어야 하겠지만, Thread B때문에 2가 되었고 거기서 1을 증가한 후에 돌려주기

때문에 3을 돌려주는 결과가 되버렸습니다.

, Even객체는 항상 짝수를 돌려주지 못할 수 있습니다. 즉 경쟁하는 Thread환경에서

공유당하는 Even객체는 안전하지 않다는 것입니다.

 

/**

* Even객체를 이용하려고 하는 경쟁하는 Thread입니다.

*/

public class CompetingThread {

    public static void main(String[] args) {

        final Even even = new Even();           

       

                           new Thread(new Runnable() {

                                      public void run() {

                                                   for(long i = 0; i < 10000000; i++) {

                                                                if (even.next() % 2 != 0)      

                                                                                System.out.println("Opps ! Not Even");

                                                     }

                              }

                         }).start();

            

                 new Thread(new Runnable() {

                     public void run() {

                                                   for(long i = 0; i < 10000000; i++) {

                                                                if (even.next() % 2 != 0)      

                                                                                System.out.println("Opps ! Not Even");

                                                     }

                     }

                 }).start();

    }

}

예제 8 - 3 CompetingThread.java

 

예제 8 - 3 CompetingThread 2개의 Thread가 경쟁해서 한 개의 Even객체의 next()

메소드를 호출하고 있습니다.  물론 대개의 경우는 짝수를 얻을 수 있을 것입니다.

하지만 수만번, 수백만번, 수천만번 실행하다보면 한 두번씩 홀수를 얻을 수 있습니다.

물론 이것은 결코 일어나서는 안되는 사건이지요.

 

 

사용자 삽입 이미지

[그림 8-2]
 
그림 8 - 2에서 보듯이 Even객체는 다중 Thread환경에서는 사용해서는 안됩니다.

경쟁하는 Thread들이 동시에 사용하는 객체는 기본적으로 다중 Thread 환경에서 객체

스스로의 상태가 Thread들의 동시다발적인 메소드 호출로 인해서 객체의 상태가 어긋날 수

있습니다. 이런 이유로 객체는 경쟁하는 Thread로부터 객체의 상태가 어긋나지 않도록

스스로를 보호하는 방법을 확보하고 있어야 합니다.

 

Posted by
,