2016-12-07 5 views
0

이 코드에서 동시성 오류를 찾을 수 있습니까? 이 코드는 한 스레드에서 완벽하게 작동하지만 두 스레드를 동시에 시작하고 addScore 메서드를 호출하면 트리 맵에 중복 요소가 추가됩니다.동시에 액세스 할 때 트리 맵의 중복 키

class User implements Runnable 
{ 

    private ScoreServiceImpl scoreService=ScoreServiceImpl.getInstance(); 

    CountDownLatch latch; 
    public User(CountDownLatch latch) 
    { 
     this.latch = latch; 
    } 

    @Override 
    public void run() { 


     for(int i=0;i<5;i++) { 
      scoreService.addScore(3,Integer.parseInt(Thread.currentThread().getName()),ThreadLocalRandom.current().nextInt(50000)); 
     } 
     System.out.println(scoreService.getHighScoreList(3)); 
    } 
} 

그리고 만들 수있는 주요 방법 : 이것은 사용자 제작 요청을 시뮬레이션하기 위해 사용하고 코드가

public final class UserHighScore implements Comparable<UserHighScore>{ 

    private final int userId; 
    private final int value; 

    public UserHighScore(int userId, int value) { 
     this.userId = userId; 
     this.value = value; 
    } 

    public int getUserId() { 
     return userId; 
    } 

    public int getValue() { 
     return value; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (obj == this) return true; 
     if (!(obj instanceof UserHighScore)) { 
      return false; 
     } 
     UserHighScore userHighScore = (UserHighScore) obj; 
     return userHighScore.userId==userId; 
    } 

    @Override 
    public int compareTo(UserHighScore uh) { 
     if(uh.getUserId()==this.getUserId()) return 0; 
     if(uh.getValue()>this.getValue()) return 1; 
     return -1; 
    } 
} 

다음과 같이

comparedTO의 오버라이드를 가진 POJO이다 스레드는 다음과 같습니다 :

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

    SpringApplication.run(RestclientApplication.class, args); 

    CountDownLatch latch = new CountDownLatch(1); 
    User user1=new User(latch); 
    User user2=new User(latch); 
    Thread t1=new Thread(user1); 
    Thread t2=new Thread(user2); 
    t1.setName("1"); 
    t2.setName("2"); 
    t1.start(); 
    t2.start(); 
    //latch.countDown(); 
} 
+0

중복 된 내용이지도에 어떻게 표시되는지 어떻게 알 수 있습니까? –

+0

왜냐하면 내가 디버깅 할 때 동일한 userId에 대해 하나 이상의 키가있는지도를보고 목록을 인쇄 할 때 나는 그것을 볼 수 있습니다. – fgonzalez

+0

나를 위해 userId 이상의 엔트리가 있어서는 안됩니다 (점수는 중요하지 않음).) 그러나 하나의 스레드에서만 잘 작동하지만 두 개에서는 작동하지 않습니다. – fgonzalez

답변

3

당신의 compareTo는 실수입니다. 당신은이 가운데 첫 번째에있는 사람을 확인, 분할에 의해 작업을 설정이

ScoreServiceImpl.getInstance().addScore(0,1,4); 
    ScoreServiceImpl.getInstance().addScore(0,1,12); 
    ScoreServiceImpl.getInstance().addScore(0,0,10); 
    ScoreServiceImpl.getInstance().addScore(0,0,3); 

나무처럼 뭔가 같은 결과는 단일 스레드받을 정복 할 수 있습니다. (1, 4가 될 것입니다) userIds가 일치하지 않으므로 비교하지 않고 값을 비교합니다. 그것이 남아 사라했을 아이디를 비교했지만 대신 한

당신은 하나 항상 두 값을 비교할 수 있습니다

또는 항상의 사용자 ID와 권리만을 비교 항목을 가면 단지에 비교 userId를하지만 당신은 할 수 '앞뒤로 전환하지 마라.

@Override 
public int compareTo(UserHighScore uh) { 
    return Integer.compare(userId, uh.userId); 
} 
+0

답변에 따르면 사용자 정의 개체를 트리 맵에서 키로 사용하여 키의 단일성과 순서를 동시에 가질 수 없습니다 , 맞지? – fgonzalez

+2

@ fgonzalez 가능하면 올바른 compareTo 구현이 필요하다. –

+0

하지만 한 필드 또는 다른 필드 만 비교할 수 있으면 정확한 구현이 표시되지 않습니다. 반환 값으로 Integer.compare (userId, uh.userId)가 반환됩니다. 그렇지 않으면 Integer.compare (value, uh.value)를 반환합니다. 내 compareTo 구현. 첫 번째 경우 키는 고유하지만 정렬되지 않으며 두 번째 경우에는 정렬되고 복제되지만 키를 참조하십시오. – fgonzalez