简体   繁体   English

在ConcurrentHashMap中列出

[英]List in ConcurrentHashMap

public static ConcurrentHashMap<Integer,Session> USER_SESSIONS...    

Everything works fine. 一切正常。 But what if the system is allowed to be authorized two sessions with the same user ID? 但是如果允许系统被授权两个具有相同用户ID的会话呢? Well, that is roughly two PCs sitting under the same account, but different session. 嗯,大约有两台PC坐在同一个帐户下,但会话不同。 Tried to do so: 试图这样做:

ConcurrentHashMap<Integer,List<Session>> USER_SESSIONS....

...............

private void addUser(Session session){
   List<Session> userSessions = Server.USER_SESSIONS.get(session.mUserId);

   if(userSessions==null){
       userSessions = new List<Session>();
       userSessions.add(session);
       Server.USER_SESSIONS.put(session.getUserId(),userSessions);
   }else{
       userSessions.add(session);
   }
}

private void removeUser(Session session){
  List<Session> userSessions = Server.USER_SESSIONS.get(session.mUserId);

  if(userSessions!=null){
       userSessions.remove(session);
       if(userSessions.size()==0)
       {
           Server.USER_SESSIONS.remove(session.getUserId());
       }
  }
}

.................

private void workWithUsers(int userId){
    for(Session session : Server.USER_SESSIONS.get(userId))
    {
       <do it!>
    }
}

Naturally, all these methods can be called from different threads, and I'm getting errors related to List . 当然,可以从不同的线程调用所有这些方法,并且我得到与List相关的错误。 And this is natural, because while I have foreach-list session can be removed by removeUser from another thread. 这很自然,因为虽然我有foreach-list会话可以通过removeUser从另一个线程中删除。 What to do? 该怎么办? How to make so that-be at work with a list of all the threads List waited until it occupies the thread is finished with it? 如何使 - 在所有线程的列表中工作列表等待它占用线程完成后呢? Yet done so :) 然而这样做:)

public static ConcurrentHashMap<Integer,ConcurrentHashMap<Session,Session>> USER_SESSIONS

Since ConcurrentHashMap thread safe. 由于ConcurrentHashMap线程安全。 But I think it's crooked decision. 但我认为这是一个歪曲的决定。 Many thanks in advance for your help! 非常感谢您的帮助!

PS: JRE 1. 6 PS:JRE 1. 6

Please sorry for my English. 抱歉我的英语。

You could use List myList = Collections.synchronizedList(new ArrayList<String>()); 您可以使用List myList = Collections.synchronizedList(new ArrayList<String>()); if you don't want to use CopyOnWriteArrayList. 如果您不想使用CopyOnWriteArrayList。

The only thing you need to have in mind is that it is mandatory to synchronized the code where you will be iterating over the list. 您唯一需要记住的是, 必须同步代码,您将在列表上进行迭代。 You can see more info here: Collections.synchronizedList and synchronized 您可以在此处查看更多信息: Collections.synchronizedList和synchronized

Using a thread safe list is still not enough to prevent race conditions in this case. 在这种情况下,使用线程安全列表仍然不足以防止竞争条件。 There's a possibility that two addUser calls at the same time may overwrite each other's puts. 同时有两个addUser调用可能会覆盖彼此的put。 Also an add could happen between the check for size and the remove call in remoeUser. 另外,在检查大小和remoeUser中的remove调用之间可能会发生添加。

You need something like this (not tested). 你需要这样的东西(未经测试)。 This code assumes that a session will not be removed before a call to it's add. 此代码假定在调用它之前不会删除会话。

    private void addUser(Session session) {
    while (true) {
        List<Session> userSessions = Collections.synchronizedList(new ArrayList<Session>());
        List<Session> oldSessions = USER_SESSIONS.putIfAbsent(session.mUserId, userSessions);
        if (oldSessions != null) {
            userSessions = oldSessions;
        }
        userSessions.add(session);

        // want to make sure the map still contains this list and not another
        // so checking references
        // this could be false if the list was removed since the call to putIfAbsent
        if (userSessions == USER_SESSIONS.get(session.mUserId)) {
            break;
        }
    }
}

private void removeUser(Session session) {
    List<Session> userSessions = USER_SESSIONS.get(session.mUserId);
    if (userSessions != null) {
        // make whole operation synchronized to make sure a new session is not added
        // after the check for empty
        synchronized (userSessions) {
            userSessions.remove(session);
            if (userSessions.isEmpty()) {
                USER_SESSIONS.remove(session.mUserId);
            }
        }
    }
}

您可以尝试在您的情况下使用CopyOnWriteArrayListCopyOnWriteArraySet

use List myList = Collections.synchronizedList(new ArrayList<String>()); 使用List myList = Collections.synchronizedList(new ArrayList<String>()); will be better 会更好
But if there is much more read operation than write, you can also use CopyOnWriteArrayList which is safe to iterate. 但是如果有比read更多的读操作,你也可以使用可以安全迭代的CopyOnWriteArrayList

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM