【问题标题】:Java synchronization with entries in a static MapJava 与静态 Map 中的条目同步
【发布时间】:2011-12-04 16:39:28
【问题描述】:

我有一个静态地图,我需要同步访问。地图由用户 ID 键入。我想优化同步,这样我就不会阻塞所有线程,我只能阻塞与同一用户 ID 相关的线程。

private static Object s_lock = new Object();
private static Map<String,User> s_users = new HashMap();
...
private someMethod() {
    synchronized(s_lock)
    {
        // keeping the global lock for as little as possible
        user=getMapEntry();
    }
    synchronized(user)   <-------- (1)
    {
        // time consuming operation
        // hopefully only blocking threads that relate to same user id.
    }
}
...
private User getMapEntry(String userId)
{
    if (s_users.containsKey(userId)) {
        user = s_users.get(userId);
    }
    else {
        user = new User();
        user.id = userId;
        s_users.put(userId, user);
    }
    return user;
}

我的问题是 - 在 (1) 我假设我没有持有“全局”同步锁,但由于 s_users 映射是静态的,因此条目是否有效静态,这意味着我仍然持有全局锁(即在类对象上同步)?

【问题讨论】:

  • 您可以使用 ConcurrentHashMap.putIfAbsent() 避免第一个同步块。

标签: java static synchronization


【解决方案1】:

不,你很好:每个地图条目都是一个单独的对象,因此在地图条目上同步不会在地图上同步,也不会在拥有地图的类上同步。

(顺便说一下,您的#getMapEntry(...) 方法实际上返回一个,而不是一个条目。映射条目包含对键的引用和一个值。)

【讨论】:

  • 如果 User 是 map 的 key 怎么能改变那个 key 的结构呢?这怎么不安全?
  • 他不插入和获取用户?
  • (注意:Hemal Pandya 和 MasterCassim 正在回复我的答案的早期版本,这是完全错误的:我完全误读了这个问题。)
【解决方案2】:

你没有在类对象上同步,但我正在做你认为它正在做的事情:

  • 不同的线程正在使用共享的s_lock 访问映射。但我认为你也可以在上面制作 s_users finalsynchronize

  • 不同线程访问同一个User对象执行“耗时操作” "按顺序排列。

我看到的唯一要注意的是,如果对象在地图中不断更改为相同的User。只要一个userId 对应一个User 对象就可以了。

我在userId.intern() 上做了类似但同步的事情。它有它的缺点,但对我来说效果很好。

【讨论】:

    【解决方案3】:

    这应该有效;第二个块仅在此用户上同步。

    小测试:

    public class Test {
    
        protected class User {
            String id;
        }
        private static Object s_lock = new Object();
        private static Map<String,User> s_users = new HashMap<String, User>();
    
        public void someMethod(String id) throws InterruptedException {
            User user;
            synchronized(s_lock)
            {
                // keeping the global lock for as little as possible
                user = getMapEntry(id);
            }
            synchronized(user)
            {
                System.out.println("Waiting for user: "+user.id);
                Thread.sleep(10000);
            System.out.println("Finished waiting for user: "+user.id);
            }
        }
    
        private User getMapEntry(String userId)
        {
            User user;
    
            if (s_users.containsKey(userId)) {
                user = s_users.get(userId);
            }
            else {
                user = new User();
                user.id = userId;
                s_users.put(userId, user);
            }
    
            return user;
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Test test = new Test();
    
            for(int i = 0; i < 10; i++) {
                final int j = i;
    
                new Thread() {
                    public void run() {
                        try {
                            test.someMethod(String.valueOf(j % 5));
                        } catch (InterruptedException e) {}
                    }
                }.start();
            }
        }
    }
    

    输出:

    Waiting for user: 0
    Waiting for user: 1
    Waiting for user: 3
    Waiting for user: 2
    Waiting for user: 4
    Finished waiting for user: 0
    Finished waiting for user: 1
    Waiting for user: 1
    Waiting for user: 0
    Finished waiting for user: 3
    Finished waiting for user: 2
    Waiting for user: 3
    Waiting for user: 2
    Finished waiting for user: 4
    Waiting for user: 4
    Finished waiting for user: 0
    Finished waiting for user: 1
    Finished waiting for user: 3
    Finished waiting for user: 2
    Finished waiting for user: 4
    

    所以没关系。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多