2016-10-13 2 views
0

코드 블록을 잠 그거나 잠금 해제하는 작은 스 니펫을 작성하려고합니다. acquire_lock와 RELEASE_LOCK 기능은 다음과 같습니다 :java 멀티 스레드 획득 잠금이 작동하지 않습니다.

public static void acquire_lock(long timestamp) { 
    synchronized(operations) { 
     // put the timestamp into queue 
     operations.add(timestamp); 
     // check if the head of queue is current timestamp, if not, 
     // this means there are some other operations ahead of current one 
     // so current operation has to wait 
     while (operations.peek() != timestamp) { 
      try { 
       operations.wait(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

public static void release_lock() { 
    synchronized(operations) { 
     // poll the finished operation out of queue 
     // and wake up all waiting operations 
     operations.poll(); 
     operations.notifyAll(); 
    } 
} 

을하지만 테스트 환경에이 코드를 삽입 할 때, 항상 잘 작동하지 않습니다, 전체 테스트 코드는 다음과 같습니다 :

public class AcquireLockNotWork { 

static int balance = 0; 
static PriorityQueue<Long> operations = new PriorityQueue<Long>(); 

// withdraw money from balance 
public static void withdraw(final int amt) { 
    // get system time 
    Long timestamp = System.nanoTime(); 
    Thread t = new Thread(new Runnable() { 
     public void run() { 
      // try to use acquire_lock to lock this piece of code 
      acquire_lock(timestamp); 
      try {  
       Thread.sleep(500); 
       int holdings = balance; 
       balance = holdings - amt; 
       System.out.println("Withdrew " + amt + " from funds. Now at " + balance); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } finally { 
       release_lock(); 
      }  
     } 
    }); 
    t.start(); 
} 

//put money into banlance 
public static void deposit(int amt) { 
    Thread t1 = new Thread(new Runnable() { 
     public void run() { 
      Long timestamp = System.nanoTime(); 
      acquire_lock(timestamp); 
      int holdings = balance; 
      balance = holdings + amt; 
      System.out.println("deposit " + amt + ", balance: " + balance); 
      release_lock(); 
     } 
    }); 
    t1.start(); 
} 

public static void acquire_lock(long timestamp) { 
    synchronized(operations) { 
     // put the timestamp into queue 
     operations.add(timestamp); 
     // check if the head of queue is current timestamp, if not, 
     // this means there are some other operations ahead of current one 
     // so current operation has to wait 
     while (operations.peek() != timestamp) { 
      try { 
       operations.wait(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

public static void release_lock() { 
    synchronized(operations) { 
     // poll the finished operation out of queue 
     // and wake up all waiting operations 
     operations.poll(); 
     operations.notifyAll(); 
    } 
} 

public static void test1() { 
    balance = 0; 
    deposit(2000); 
    withdraw(500); 
    withdraw(1000); 
} 

public static void main(String[] args) { 
    test1(); 
} 
} 
작은 횟수 대

이 결과는 다음과 같을 것이다 :

deposit 2000, balance: 2000 
Withdrew 500 from funds. Now at 500 
Withdrew 1000 from funds. Now at 500 

acquire_lock 및 RELEASE_LOCK 푸 즉 의무가 잘 작동하지 않습니다. 마지막 2 개의 스레드 (withdrew 500 및 withdru 1000)가 acquire_lock()과 release_lock() 사이의 블록에 동시에 들어갔고, 원하는 것이 아닌 것으로 보입니다. acquire_lock 및 release_lock 함수의 문제점은 무엇입니까?

+0

코드가 동일한 값을 두 번 이상 반환하지 않는 System.nanoTime()에 의존하는 것으로 보이지만 그 동작은 보장되지 않습니다. System.nanoTime()의 Javadoc은 "이 메서드는 나노초 정밀도를 제공하지만 반드시 나노초 해상도 (즉, 값 변경 빈도)가 아닙니다"라고 말합니다. –

답변

0

여기는 매우 까다 롭습니다. 비정상적인 현상은 후자의 스레드가 먼저 acquire_lock에 들어가기 때문에 발생합니다. 그리고 이전 스레드가 acquire_lock로 들어가면 코드가 스레드의 타임 스탬프를 기반으로 블록하기 때문에 블록되지 않습니다. 따라서 두 스레드는 동일한 코드 영역으로 이동합니다.