多线程区块链场景下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
相关产品推荐
相关产品推荐

