Дублированные ключи в древовидной карте при одновременном доступе

Может ли кто-нибудь найти ошибку параллелизма в этом коде? Код прекрасно работает для одного потока, но как только я запускаю 2 потока одновременно и вызываю метод addScore, он добавляет дубликаты в древовидную карту.

Пойо с переопределенным сравнению является следующим:

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;
    }
}

Это код, который я использую для имитации запросов пользователя:

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 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();
}

1 ответ

Решение

Ваше сравнение с вином. Вы можете получить тот же результат однопоточным с чем-то вроде этого

    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 не совпадают, он не сравнивает их, а сравнивает значения. если бы он сравнил идентификаторы пользователей, он пошел бы налево, но вместо этого он пошел направо и сравнил только элементы с идентификатором пользователя, равным единице.

Вы можете всегда сравнивать оба значения или всегда сравнивать только по идентификатору пользователя, но вы не можете переключаться между ними.

@Override
public int compareTo(UserHighScore uh) {
    return Integer.compare(userId, uh.userId);
}
Другие вопросы по тегам