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

如何基于条件阻塞线程?多线程调用方法场景技术咨询

Alright, let's tackle this thread blocking problem for your notifyCompleted method. From the code snippet you shared, I assume the core need is to handle concurrent calls safely—probably blocking threads when they try to modify the same player's ranked data, or waiting for specific business conditions to be met before proceeding. Let's break down practical, actionable solutions:

1. Fine-Grained Locking by Player ID (Prevent Race Conditions)

If your primary goal is to ensure serial execution of operations for the same player (avoiding concurrent modifications to PlayerRankedData), using a per-player lock is the way to go. This lets threads handling different players run in parallel, while blocking threads that target the same player.

Here's how to implement it:

// Class-level map to hold locks for each player (thread-safe via ConcurrentHashMap)
private final ConcurrentHashMap<Long, ReentrantLock> playerLocks = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, PlayerRankedData> rankedDataMap; // Assume this is thread-safe
private final int minimumMatchPoints;

public void notifyCompleted(PlayerDTO winner, PlayerDTO loser) {
    // Get or create a lock for each player atomically
    ReentrantLock winnerLock = playerLocks.computeIfAbsent(winner.getId(), k -> new ReentrantLock());
    ReentrantLock loserLock = playerLocks.computeIfAbsent(loser.getId(), k -> new ReentrantLock());

    // Critical: Lock in a fixed order to avoid deadlocks!
    long winnerId = winner.getId();
    long loserId = loser.getId();
    ReentrantLock firstLock = winnerId < loserId ? winnerLock : loserLock;
    ReentrantLock secondLock = winnerId < loserId ? loserLock : winnerLock;

    try {
        firstLock.lock();
        secondLock.lock();

        // Your original business logic, now safe from concurrent modifications
        PlayerRankedData winnerData = rankedDataMap.get(winner.getId());
        PlayerRankedData loserData = rankedDataMap.get(loser.getId());
        
        int pointsToAdd = getPointsToAdd(winnerData, loserData);
        int pointsToDeduct = getPointsToDeduct(loserData, winnerData);
        
        winnerData.addMatchPoints(pointsToAdd);
        loserData.deductMatchPointsUptoMinimum(pointsToDeduct, minimumMatchPoints);
        winnerData.addRewards(/* Your reward logic here */);
        // ... rest of your method
    } finally {
        // Always release locks in the finally block to avoid leaks
        secondLock.unlock();
        firstLock.unlock();
    }
}

Key Notes:

  • computeIfAbsent ensures we create a lock for a player only once, atomically.
  • Locking by player ID order (smaller ID first) eliminates deadlocks that could happen if two threads try to lock players A+B and B+A at the same time.
  • This maintains good concurrency: threads handling unrelated players won't block each other.
2. Conditional Blocking with Condition (For Custom Business Rules)

If you need to block threads until a specific business condition is met (e.g., the player isn't in an active match, or their data is in a valid state), combine ReentrantLock with Condition to wait and wake threads as needed.

Example implementation for waiting until a player is not in a match:

private final ConcurrentHashMap<Long, PlayerLockCondition> playerLockConditions = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, PlayerRankedData> rankedDataMap;
private final int minimumMatchPoints;

// Helper class to wrap lock and condition per player
private static class PlayerLockCondition {
    final ReentrantLock lock = new ReentrantLock();
    final Condition playerReady = lock.newCondition();
}

public void notifyCompleted(PlayerDTO winner, PlayerDTO loser) throws InterruptedException {
    PlayerLockCondition winnerLockCond = playerLockConditions.computeIfAbsent(winner.getId(), k -> new PlayerLockCondition());
    PlayerLockCondition loserLockCond = playerLockConditions.computeIfAbsent(loser.getId(), k -> new PlayerLockCondition());

    // Again, lock in fixed order to prevent deadlocks
    long winnerId = winner.getId();
    long loserId = loser.getId();
    PlayerLockCondition firstLockCond = winnerId < loserId ? winnerLockCond : loserLockCond;
    PlayerLockCondition secondLockCond = winnerId < loserId ? loserLockCond : winnerLockCond;

    try {
        firstLockCond.lock.lock();
        secondLockCond.lock.lock();

        PlayerRankedData winnerData = rankedDataMap.get(winner.getId());
        PlayerRankedData loserData = rankedDataMap.get(loser.getId());

        // Wait until the player is ready (e.g., not in a match)
        while (winnerData.isInMatch()) {
            winnerLockCond.playerReady.await(); // Blocks until signaled
        }
        while (loserData.isInMatch()) {
            loserLockCond.playerReady.await();
        }

        // Execute your business logic once conditions are met
        int pointsToAdd = getPointsToAdd(winnerData, loserData);
        int pointsToDeduct = getPointsToDeduct(loserData, winnerData);
        
        winnerData.addMatchPoints(pointsToAdd);
        loserData.deductMatchPointsUptoMinimum(pointsToDeduct, minimumMatchPoints);
        winnerData.addRewards(/* Your reward logic here */);
        // ... rest of your method

        // Notify waiting threads that the player's state has changed
        winnerLockCond.playerReady.signalAll();
        loserLockCond.playerReady.signalAll();
    } finally {
        secondLockCond.lock.unlock();
        firstLockCond.lock.unlock();
    }
}

// Example method to check player readiness
private boolean isPlayerReady(PlayerRankedData data) {
    return !data.isInMatch();
}

Key Notes:

  • Use a while loop to re-check conditions after waking up—threads can be woken spuriously without the condition being met.
  • signalAll() wakes all threads waiting on the condition, which is safer than signal() (which only wakes one thread) if multiple threads might be waiting.
  • This is flexible enough to adapt to any custom condition you need (e.g., waiting for a player's cooldown to expire).
Additional Best Practices
  • Ensure rankedDataMap is thread-safe (use ConcurrentHashMap instead of a regular HashMap).
  • Clean up unused locks: If players can be removed from the system, remove their lock entries from the map to avoid memory leaks.
  • Avoid global locks—they kill concurrency. Stick to fine-grained per-player locks whenever possible.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:55:19