并发访问时树图中的重复键

Duplicated keys in Tree Map when accesing concurrently

谁能找出这段代码中的并发错误?该代码对于一个线程来说工作得很好,但是一旦我同时启动 2 个线程并调用 addScore 方法,它就会向树 Map 添加重复项。

覆盖了compareTO的pojo如下:

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

你的 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 不匹配,因此不比较它们而是比较值。如果它比较了 userids,它会向左走,但它向右走,只比较 userid 为 one

的项目

您可以 always 比较两个值或 always 只比较 userId,但不能来回切换。

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