【问题标题】:Encapsulate ConcurrentHashMap but still be concurrent封装 ConcurrentHashMap 但仍然是并发的
【发布时间】:2020-08-18 04:26:43
【问题描述】:

对于一个班级项目,我使用 concurrentHashMap 来包含活动用户列表及其连接详细信息。多个线程应该能够获取所有活动用户的列表,其中 5 个线程能够修改 hashMap。当我测试它时,它似乎可以工作,但我不知道线程是否被阻塞并且系统是否足够快。

  • 想封装concurrentHashMap,不知道会不会停止并发?
  • 我还希望只有一个版本的 hashmap,所以我将类设为枚举,太糟糕了,只需要 main 来创建它?
  • 我不希望其他线程修改 hashMap,所以我没有传递集合/迭代器。有没有办法在没有删除的情况下传递集合?
  • (new) 我在 CoolFeature 中调用了两个方法,一个是阻塞的,一个不是。两者都可以有自己的位置。论文有什么问题吗?

我们使用 Akka Actors 来创建线程和处理并发,我的简化代码如下。这不是作业问题,只是我在项目中创建的部分内容。

编辑:我更改了代码,最好有一个不依赖 Akka 的解决方案,以便其他人可以找到有用的答案。

package server.management;
public class Sessions {
  static final ConcurrentHashMap<Integer, Id<User>> sessions = new concurrentHashMap<>();
  private Sessions() { }
}

public class SessionManager {
  public static void create(int connection, Id<User> userId) {
    // checks
    Sessions.sessions.put(connection, userId);
  }

  public static Id<User> getUser(int connection) {
    Id<User> userId = Sessions.sessions.get(connection);
    return userId;
  }

  public static ArrayList<Id<User>> getActiveUsers() {
    ArrayList<Id<User>> list = new ArrayList<>();

    // Don't pass the sessions.values() directly because the Map can be modified.
    for (Id<User> userId : Sessions.sessions.values()) {
      list.add(userId);
    }
    return list;
  }

  // other methods
}

/* Another thread/akka actor class */
package server;
import server.management.SessionManager;

public class CoolFeature extends AbstractActor {
  ...
  Id<User> client = SessionManager.getUser(1023);
  // do stuff
  CompletableFuture<ArrayList<Id<User>>> futureList =
      CompletableFuture.supplyAsync(SessionManager::getActiveUsers);
  // do stuff
  ArrayList<Id<User>> list = futureList.get();
  // do stuff
}

【问题讨论】:

    标签: java multithreading akka concurrenthashmap


    【解决方案1】:

    使用akka actor,您可以将一个普通的Map作为状态放入一个actor内部,而无需担心并发访问,因为使用actor模型,一个actor一次只处理一条消息。您可以执行以下操作。代码在 Scala 中,但你明白我的意思。

    object Manager {
      case class Get(id: Int)
      case object GetActive
      case class Create(connId: Int, userId: Int)
    }
    
    class Manager extends Actor {
      import Manager._
      val sessions = mutable.Map[Int, Int].empty
      override def receive = {
        case Get(id) => sessions.get(id)
        case GetActive => sessions.values
        case Create(connId, userId) => sessions += (connId -> userId)
      }
    }
    

    如果您有任何问题,请告诉我。

    【讨论】:

    • 对于一个actor内部的代码(当他们说“使用Akka Actors创建线程”时我认为OP的意思)来操纵其他actor正在操纵的相同状态通常也是一个坏主意.
    • 谢谢,创建一个演员会更容易。我只是觉得这个演员可能会收到很多请求,可能会成为瓶颈。 @LeviRamsey 是的, concurrentHashMap 在多个线程访问它时会锁定地图的各个部分。我希望这会分散负载,所以它不是一个大瓶颈。
    • CHM 的锁定将是值得信赖的(它已经被 JVM 应用程序进行了多年的实战测试,即使我完全不知道正确性的正式证明)。然而,没有什么能阻止一个演员从另一个演员那里拉出地毯。如果您担心瓶颈,我会考虑使用分片 Actor 实现并发映射(可能是集群分片持久 Actor,特别是如果您可能希望跨多个实例运行 HA 等)
    • @dean 正如 Levi 建议的那样,可以做分片演员,例如如果有大量请求,则集群分片参与者。始终可以从简单的单参与者解决方案开始,并以近似负载对其进行测试。
    猜你喜欢
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2015-07-26
    • 1970-01-01
    • 1970-01-01
    • 2020-10-11
    • 2021-07-30
    • 1970-01-01
    相关资源
    最近更新 更多