【问题标题】:Multiple Threads accessing a non synchronised Map访问非同步映射的多个线程
【发布时间】:2014-08-27 17:51:30
【问题描述】:

我面临这个问题,我有两个 A 和 B 类,“A 类”有两个线程 t1 和 t2,它试图访问“B 类”映射,如下所示,现在问题是,我无法进行任何更改在 B 类地图上,因此我不能使用 synchronised 关键字,或者我不能把它放在同步方法中。所以我的疑问是,有没有其他方法可以同步这张地图。

public class A{

    static B b = new B();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {

                Map<Integer,String> map = Collections.synchronizedMap(b.getMap());
                map.put(1, "one");
                map.put(2, "two");
                map.put(3, "three");
                b.setMap(map);
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {

                Map<Integer,String> map = Collections.synchronizedMap(b.getMap());
                map.put(4, "four");
                map.put(5, "five");
                map.put(6, "six");
                b.setMap(map);

            }
        });

        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        for(Map.Entry<Integer, String> entry : b.getMap().entrySet()){
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }     
    }

我使用LinkedHashMap,只是为了跟踪插入顺序(忽略性能问题)

public class B{

    private Map<Integer,String> map = new LinkedHashMap<Integer,String>();

    public Map<Integer,String> getMap() {
        return map;
    }

    public void setMap(Map<Integer,String> map) {
        this.map = map;
    }
} 

【问题讨论】:

  • 如我所见,您可以选择从 B 公开 setMap,只需在 A 中创建并发哈希图,然后将其设置为 B。

标签: java multithreading synchronization synchronized


【解决方案1】:

您可以创建一次同步的Map 并让两个线程使用该Map 实例。

final Map<Integer,String> map = Collections.synchronizedMap(b.getMap());
b.setMap(map);

Thread t1 = new Thread(new Runnable() {

    @Override
    public void run() {
        map.put(1, "one");
        map.put(2, "two");
        map.put(3, "three");
    }
});

Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        map.put(4, "four");
        map.put(5, "five");
        map.put(6, "six");
    }
});

【讨论】:

    【解决方案2】:

    为什么不在B中设置地图。如果您不能使用ConcurrentHashMap,那么您可以使用Collections.synchronizedMap(new LinkedHashMap&lt;&gt;())。任何外部同步都会留下不同步访问地图的可能性。

    
    public class A{
    
        static B b;
        static {
            b = new B();
            b.setMap(new ConcurrentHashMap());
        }
        //...
    }
    

    【讨论】:

    • synchronizedMap 也有同样的可能:例如,从它获取的keySet.iterator() 是不同步的,因此它不是线程安全的,需要外部同步。 ConcurrentMap 在这方面要好得多,保证在迭代器获取/第一次调用 next()/hasNext() 时存在的快照上的稳定迭代器。
    • 是的,ConcurrentHashMap 更好,我只添加了 LinkedHashMap 示例,因为 op 声明了 I am using LinkedHashMap, only to track the order of insertion,这在 CHM 中是不可能的。
    【解决方案3】:

    正如其他人所指出的,您可以使用同步包装器,java.util.Collections 提供了一种方便的方法来获取它。但是,让每个线程都有自己的同步包装器对您没有帮助——所有参与者都需要使用相同的同步工具。

    更一般地说,如果您需要同步对一些不太方便的对象的访问,那么您可以使用外部同步。在您的情况下,可能如下所示:

    public class A{
    
        static B b = new B();
        // will use this object to synchronize access to b's map:
        static Object bLock = new Object();
    
        public static void main(String[] args) {
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    Map<Integer,String> map = b.getMap();
    
                    synchronized(bLock) {
                        map.put(1, "one");
                        map.put(2, "two");
                        map.put(3, "three");
                    }
                    // no need for setMap()
                }
            });
    
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    Map<Integer,String> map = b.getMap();
    
                    synchronized (bLock) {
                        map.put(4, "four");
                        map.put(5, "five");
                        map.put(6, "six");
                    }
                    // no need for setMap()
                }
            });
    
            // ...
        }
    }
    

    包 java.util.concurrent.locks 有一些你也可以使用的锁类,但通常你不需要这么花哨的东西。

    【讨论】:

      【解决方案4】:

      简单地说:

      public class A{
      
          static B b;
          static Map bMap;
      
          static {
              b = new B();
              bMap = Collections.synchronizedMap(b.getMap());
          }
      
          ... //now use bMap instead
      }
      

      【讨论】:

      • OP 声明 “我无法对 B 类地图进行任何更改”
      猜你喜欢
      • 2013-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多