对java并发编程实战中ConcurrentHashMap不能加锁实现独占访问的理解

    xiaoxiao2022-07-03  128

    java并发编程实战中5.2.2节写到由于ConcurrentHashMap不能被加锁来执行独占式访问,因此我们无法使用客户端加锁来创建新的原子操作。 一开始无法理解这句话的描述,因为如果有一个ConcurrentHashMap对象,在所有使用这个对象的地方都对这个对象客户端加锁,那么也能保证这个对象复合操作的原子性;后来才明白他想说的其实是即使使用客户端加锁,也存在无法保证ConcurrentHashMap复合操作的原子性的情况,也就是无法保证执行独占式访问,具体看下面例子

    ConcurrentHashMap

    public class ConcurrentHashMapTest { private static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>(16); public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (map) { for (int i = 0; i < 10; i++) { System.out.println("Thread1" + i) if (!map.containsKey(i)) { System.out.println("map:" + map.put(i,i) + " i:" + i); } } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("thread2" + i) map.put(i,99); } } }); t1.start(); t2.start(); } }

    展示一种破坏了原子性的结果: 这里我们对线程1的put-if-absent操作加上客户端锁;出现了线程1执行put (0,0)的时候返回了99,说明线程2已经将(0,99)put到了map里,而线程1执行put前判断了map里并不包含0这个key才执行put操作,所以这里原子性被破坏了。这是因为ConcurrentHashmap内用的是分段锁,并不是线程1中map对象的锁,下面对比下Vector

    vector

    public class ConcurrentHashMapTest { private static Vector<Integer> vector = new Vector<>(16); public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000);//修改次值可调整线程2执行次数 } catch (InterruptedException e) { e.printStackTrace(); } synchronized (vector) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.println("Thread1:" + i); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { vector.set(i,i); System.out.println("thread2:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); t1.start(); t2.start(); } }

    展示结果: 可以修改代码中注释地方sleep的值调整线程2for循环执行的次数。但不管如何调整,线程1一旦开始输出必然是连续完整的输出一串结果,这是因为Vector方法里用的synchronize同步,使用的锁是vector对象的锁,和线程1synchronize代码块是同一个锁,所以一旦进入线程1的synchronize代码块,线程2的add方法势必要等待线程1执行完才可以执行,所以Vector可以用客户端加锁保证原子性操作;

    总结

    以上是本人对ConcurrentHashMap一点微不足道的理解,如果有不对的地方欢迎指正

    最新回复(0)