You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程区块链场景下nextMessageId生成重复ID的同步问题求解

问题:nextMessageId()方法偶发返回重复ID异常

我遇到了nextMessageId()方法的异常问题:该方法原本应为每条新创建的消息返回唯一ID,但偶尔会返回已使用过的重复值,暂未定位到问题根因。尝试在IDE中调试,但单步执行时方法运行一切正常。
我正在学习多线程开发、方法正确同步等相关技术,当前正在模拟用户发送消息的业务流程。
核心业务逻辑为:每次区块挖矿成功后,消息会从缓冲区复制到当前区块,随后新区块会被添加到blocks集合中。除了nextMessageId()方法异常外,其余功能均运行正常,恳请各位帮忙排查问题。
为保证帖子简洁我未贴出冗余代码,若需要更多信息请告知我。

相关代码

UserService类

public final class UserService extends Service<User, String> {
    ...

    @Override
    public void submit(String message, User user) {
        synchronized (Blockchain.class) {
            MessageEntry messageEntry = MessageEntry.newInstance(repo.nextMessageId(), message, user);
            repo.postMessage(messageEntry);
        }
    }
}

MinerService类

public final class MinerService extends Service<Miner, Long> {
    ...

    public void submit(Long number, Miner miner) {
        if (repo.getCurrentBlock().hash(number).startsWith(repo.prefix())) {
            synchronized (Blockchain.class) {
                if (repo.getCurrentBlock().hash(number).startsWith(repo.prefix())) {
                    repo.createBlock(number, miner);
                }
            }
        }
    }
}

Blockchain类

public class Blockchain {
    ...

    private Deque<Block> blocks;
    private Deque<DataEntry<?>> messageBuffer;
    private Block currentBlock;

    ...

    public long nextMessageId() {
        return blocks.stream()
                .mapToLong(block -> block.getData().size())
                .sum() + messageBuffer.size() + 1L;
    }

    public void postMessage(DataEntry<?> dataEntry) {
        messageBuffer.offerLast(dataEntry);
    }

    public void createBlock(long number, Miner miner) {
        long duration = (new Date().getTime() - currentBlock.getTimestamp()) / 1000;
        Block.ProofOfWork proofOfWork = new Block.ProofOfWork(number, duration, updateN(duration), miner);
        currentBlock.setProofOfWork(proofOfWork);

        if (blocks.offerLast(currentBlock)) {
            currentBlock = generateBlock();
            currentBlock.setData(messageBuffer);
            messageBuffer.clear();
            stateManager.save();
        }
    }
    ...
}

错误复现输出

生成5个区块后的消息输出:

Block:
Created by miner # 4
Id: 1
Timestamp: 1637995160818
Magic number: 6489039085832314491
Hash of the previous block: 
0
Hash of the block: 
7cecb0d73c5bbfa925f2c04fba90778c8431e43dc3abd1b0faf1dbc23400321c
Block data: no messages
Block was generating for 0 seconds
N was increased to 1

Block:
Created by miner # 6
Id: 2
Timestamp: 1637995160897
Magic number: 5017000130559711273
Hash of the previous block: 
7cecb0d73c5bbfa925f2c04fba90778c8431e43dc3abd1b0faf1dbc23400321c
Hash of the block: 
0ff1a96574cd8cf9db8c91eeb436df8efd084582251c081409e43e0f17069d51
Block data: 
1 Charles: Life is good
2 Aramys: How bout' those Dolphins?
3 Evelio: Life is good
4 Armando: I love Java
5 Evelio: I love Java
6 Armando: What is the meaning of life?
7 Aramys: I love basketball
8 Charles: How bout' those Dolphins?
Block was generating for 0 seconds
N was increased to 2

Block:
Created by miner # 3
Id: 3
Timestamp: 1637995160918
Magic number: -4429177738817892095
Hash of the previous block: 
0ff1a96574cd8cf9db8c91eeb436df8efd084582251c081409e43e0f17069d51
Hash of the block: 
007577aca398b8fa711229b95f2abb0f959aa73fbaa8939516ca1bea11a467fa
Block data: no messages
Block was generating for 0 seconds
N was increased to 3

Block:
Created by miner # 5
Id: 4
Timestamp: 1637995160932
Magic number: 2352460595297940125
Hash of the previous block: 
007577aca398b8fa711229b95f2abb0f959aa73fbaa8939516ca1bea11a467fa
Hash of the block: 
00053d5c5b0e958f828c12ae74469fdce1e840334cfa4a431504239133c7c612
Block data: 
9 Evelio: How are you?
Block was generating for 0 seconds
N was increased to 4

Block:
Created by miner # 5
Id: 5
Timestamp: 1637995160951
Magic number: 3338207044781263189
Hash of the previous block: 
00053d5c5b0e958f828c12ae74469fdce1e840334cfa4a431504239133c7c612
Hash of the block: 
000093d155de9a54e2143b97d752b7d57031056ec6eb07b9672c5c0815fd9272
Block data: 
9 Armando: This chat is garbage
10 Charles: Interesting...
11 Aramys: Will I ever make decent money?
Block was generating for 0 seconds
N was increased to 5

[1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 11]

问题根因与修复方案

最终定位问题根因为计算ID时未计入currentBlock中存储的DataEntry(消息),修改后代码如下,问题已修复:

public long nextMessageId() {
    return blocks.stream()
            .mapToLong(block -> block.getData().size())
            .sum() + currentBlock.getData().size() + messageBuffer.size() + 1L;
}

内容的提问来源于stack exchange,提问作者csoler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 17:54:04