2013-05-03 1 views
7

자바에서 스레드 안전 메커니즘을 이해하려고하는데 도움이 필요합니다.Java에서 스레드 안전 전역 변수

public class ThreadSafe { 

    private Executor executor = new ScheduledThreadPoolExecutor(5); 

    private long value = 0; 

    public void method() { 
     synchronized (this) { 
      System.out.println(Thread.currentThread()); 
      this.value++; 
     } 
    } 

    private synchronized long getValue() { 
     return this.value; 
    } 

    public static void main(String... args) { 
     ThreadSafe threadSafe = new ThreadSafe(); 
     for (int i = 0; i < 10; i++) { 
      threadSafe.executor.execute(new MyThread()); 
     } 

    } 

    private static class MyThread extends Thread { 

     private ThreadSafe threadSafe = new ThreadSafe(); 

     private AtomicBoolean shutdownInitialized = new AtomicBoolean(false); 

     @Override 
     public void run() { 
      while (!shutdownInitialized.get()) { 
       threadSafe.method(); 
       System.out.println(threadSafe.getValue()); 
      } 
     } 
    } 

} 

여기가 value 스레드로부터 안전하기 위해 노력하고, 한 번에 하나 개의 스레드에서만 액세스 할 : I 클래스가 있습니다. 이 프로그램을 실행하면 synchronized 블록 내에 랩핑해도 value에 하나 이상의 스레드가 작동하는 것을 볼 수 있습니다. 물론이 루프는 무한하지만 그 단지 예를 들어, 나는 몇 초 후에 수동으로이 프로그램을 중지하고, 그래서 나는이 :

2470 
Thread[pool-1-thread-3,5,main] 
2470 
Thread[pool-1-thread-5,5,main] 
2470 
Thread[pool-1-thread-2,5,main] 

다른 스레드가 접근이 value 변경됩니다. 누군가가 내게 왜 이렇게 설명 할 수 있니? 그리고이 전역 변수를 안전하게하는 방법은 무엇입니까?

+0

이 예제에서는 긴 정적 만들 의도하지 되었습니까? 다른 경우에는 threadsafety 문제가 없기 때문입니다. –

+0

내 대답을 확인하십시오. 귀하의'MyThread' 클래스는'Thread'를 확장하지 않아야하고'Runnable'을 구현해야합니다. – Gray

답변

10

각 스레드는 고유 한 ThreadSafe을 가지고 있으며 각 ThreadSafe에는 고유 한 value이 있습니다. 또한 synchronized 메서드는 this을 잠급니다. 따라서 각 ThreadSafe은 자체적으로 잠겨 있으며 스레드간에 공유되지 않습니다. 이를 스레드 로컬이라고하며 스레드 안전을 보장하는 가장 쉬운 방법입니다. :)

원하는 실험을하려면 MyThread을 생성자의 생성자가 (건설하는 대신) ThreadSafe 인수로 변경해야합니다. 그런 다음 주 방법으로 하나의 ThreadSafe을 생성하고 건설 시간에 각각 MyThread에줍니다.

4

Runnable에 각각 ThreadSafe 클래스의 인스턴스가 있기 때문에 매번 동일한 값을 얻게됩니다.

모두 동일한 클래스를 공유하려면 ThreadSafe의 인스턴스 하나만 필요하고 모든 작업에이 인스턴스를 전달해야합니다 (아래 참조). 언급 한 바와 같이 AtomicLong은 스레드 안전 공유를 원한다면 이동하는 방법입니다. long.

또한 MyThread 클래스는 이 아니고extend Thread이되어야합니다. 대신 implements Runnable이어야합니다. Thread 이미 implements Runnable이므로 코드가 작동합니다. myThread.interrupt()을 수행 한 경우 run() 메서드를 호출하는 스레드 풀 스레드이기 때문에 실제로 스레드를 인터럽트하지 않습니다.

다음과 같은 뭔가 작동합니다 :

ThreadSafe threadSafe = new ThreadSafe(); 
for (int i = 0; i < 10; i++) { 
    threadSafe.executor.execute(new MyRunnable(threadSafe)); 
} 
... 
private static class MyRunnable implements Runnable { 
    private final ThreadSafe threadSafe; 
    public MyRunnable(ThreadSafe threadSafe) { 
     this.threadSafe = threadSafe; 
    } 
    ...