同步类容器

同步类容器

Vector、HashTable等古老的并发容器,都是使用Collections.synchronizedXXX等工厂方法创建的, 并发状态下只能有一个线程访问容器对象,性能很低(进行put或者get操作的时候之后一个线程能获取到锁)

例子

public class DemoThread26 {
   public static void main(String[] args) throws Exception {
      /*
       * 使用以下方法被包裹的类将支持多线程 
       * Collections.synchronizedCollection(c);
       * Collections.synchronizedList(list); 
       * Collections.synchronizedMap(m);
       * Collections.synchronizedSet(s); 
       * Collections.synchronizedSortedMap(m);
       * Collections.synchronizedSortedSet(s);
       */
      
      // 非线程安全的List
      final List<String> list = new ArrayList<String>();
      // 线程安全的List
//    final List<String> list = Collections.synchronizedList(new ArrayList<String>());
      ExecutorService es = Executors.newFixedThreadPool(100);

      //向list中并发加入1万个元素,如果是线程安全的那么list.size=1万,否则!=1万
      for (int i = 0; i < 10000; i++) {
         es.execute(new Runnable() {
            @Override
            public void run() {
               list.add("5");
            }
         });
      }

      es.shutdown();

      while (true) {
         if (es.isTerminated()) {
            System.out.println("所有的子线程都结束了!");
            System.out.println(list.size());
            if(list.size()!=10000){
               System.out.println("线程不安全!");
            }else{
               System.out.println("线程安全!");
            }
            break;
         }
      }
   }
}

直接使用ArrayList,结果如下

所有的子线程都结束了!
9977
线程不安全!

使用线程安全的list,结果如下

所有的子线程都结束了!
10000
线程安全!

synchronizedList源码

public static <T> List<T> synchronizedList(List<T> list) {
    return (list instanceof RandomAccess ?
            new SynchronizedRandomAccessList<>(list) :
            new SynchronizedList<>(list));
}

用的还是list,只是在调用方法之前需要去获得锁

public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
}

Vector

查看add方法,直接在方法上加锁

/**
 * Appends the specified element to the end of this Vector.
 *
 * @param e element to be appended to this Vector
 * @return {@code true} (as specified by {@link Collection#add})
 * @since 1.2
 */
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

HashTable

查看put方法,直接在方法上加锁

/**
 * Maps the specified <code>key</code> to the specified
 * <code>value</code> in this hashtable. Neither the key nor the
 * value can be <code>null</code>. <p>
 *
 * The value can be retrieved by calling the <code>get</code> method
 * with a key that is equal to the original key.
 *
 * @param      key     the hashtable key
 * @param      value   the value
 * @return     the previous value of the specified key in this hashtable,
 *             or <code>null</code> if it did not have one
 * @exception  NullPointerException  if the key or value is
 *               <code>null</code>
 * @see     Object#equals(Object)
 * @see     #get(Object)
 */
public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

并发容器

  • JDK5.0之后提供了多种并发类容易可以替代同步类容器,提升性能、吞吐量

  • ConcurrentHashMap替代HashMap、HashTable

  • ConcurrentSkipListMap替代TreeMap (和HashMap的区别是它是有序的)

  • ConcurrentHashMap将hash表分为16个segment,每个segment单独进行锁控制,从而减小了锁的 粒度,提升了性能

ConcurrentHashMap

无论怎么改还是基于可见性和原子性

HashTable 实现是在整体加锁

ConcurrentHashMap将hash表分为16个segment,每个segment单独进行锁控制,从而减小了锁的 粒度,提升了性能

例子

通过并发下的运行时间对比ConcurrentHashMap与Hashtable的性能

class DemoDemo{
   public DemoDemo() {
   }
}

public class DemoThread27 {
   
   //通过并发下的运行时间对比ConcurrentHashMap与Hashtable的性能
   public static void testMap1(){
      //final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String,Integer>();
      final Hashtable<String, Integer> map = new Hashtable<String,Integer>();
      for(int i=0;i<10;i++){
         new Thread(new Runnable() {
            @Override
            public void run() {
               Long start = System.currentTimeMillis();
               for(int i=0;i<1000000;i++){
                  map.put("a"+i, i);
               }
               System.out.println(System.currentTimeMillis()-start);
            }
         }).start();
      }
   }
   
   //对比ConcurrentSkipListMap与SortedMap的性能
   public static void testSkipListMap1() throws InterruptedException{
      
      //高性能线程安全
      //final ConcurrentSkipListMap<String, DemoDemo> skipMap = new ConcurrentSkipListMap<String,DemoDemo>();
      //线程不安全、性能高
      //final SortedMap<String, DemoDemo> skipMap = new TreeMap<String,DemoDemo>();
      //低性能线程安全
      final SortedMap<String, DemoDemo> skipMap = Collections.synchronizedSortedMap(new TreeMap<String,DemoDemo>());
      
      final CountDownLatch countDownLatch = new CountDownLatch(10);
      for(int i=0;i<10;i++){
         new Thread(new Runnable() {
            @Override
            public void run() {
               Long start = System.currentTimeMillis();
               Random rn = new Random();
               for(int i=0;i<1000;i++){
                  try {
                     skipMap.put("k"+i%10, new DemoDemo());
                     Thread.sleep(0);
                  } catch (InterruptedException e) {
                     e.printStackTrace();
                  }
               }
               System.out.println("添加100个元素耗时:"+(System.currentTimeMillis()-start)+"毫秒");
               countDownLatch.countDown();
            }
         }).start();
      }
      
      countDownLatch.await();
      //System.out.println(skipMap);
      System.out.println(skipMap.size());
   }
   
   public static void testMap2(){
      ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String,Integer>();
      map.put("a", 1);
      map.put("b", 1);
      map.put("c", 1);
      map.put("d", 1);
      //如果key已经存在则更新
      map.put("a", 2);
      System.out.println(map);
      //如果key存在则不更新,不存在则添加
      map.putIfAbsent("b", 2);
      map.putIfAbsent("e", 3);
      System.out.println(map);
   }
   
   public static void testSkipListMap2(){
      ConcurrentSkipListMap<String, Integer> map = new ConcurrentSkipListMap<String, Integer>();
      map.put("a", 1);
      map.put("b1", 1);
      map.put("c", 1);
      map.put("d", 1);
      //如果key已经存在则更新
      map.put("a", 2);
      System.out.println(map);
      //如果key存在则不更新
      map.putIfAbsent("b", 2);
      System.out.println(map);
   }
   
   public static void main(String[] args) throws Exception {
      testMap1();
//    testMap2();
      //testSkipListMap1();
//    testSkipListMap2();
   }
}

运行testMap1(),使用ConcurrentHashMap,结果如下

1116
1139
1041
1151
1153
1164
1173
1178
1180
1171

运行testMap1(),使用Hashtable,结果如下

1776
1781
1784
1793
1793
1794
1795
1796
1799
1799

同理testSkipListMap1

COW类容器

Copy On Write容器,简称COW;写时复制容器,向容器中添加元素时,先将容器进行Copy出一个新容器,然后将元素添加到新容器中,再将原容器的引用指向新容器。并发读的时候不需要锁定容器, 因为原容器没有变化,使用的是一种读写分离的思想。由于每次更新都会复制新容器,所以如果数据量较大,并且更新操作频繁则对内存消耗很高,建议在高并发读的场景下使用 (适合读多写少的情况)

CopyOnWriteArraySet基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是 CopyOnWriteArrayList的addIfAbsent方法, adIfAbsent方法同样采用锁保护,并创建一个新的大小+1的Object数组。遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。从以上分析可见,CopyOnWriteArraySet在add时每次都要进行数组的遍历,因此其性能会低于CopyOnWriteArrayList.
/**
 * CopayOnWrite并发容器构建
 * CopyOnWriteArrayList 替代 ArrayList、Vector (有序可重复的)
 * CopyOnWriteArraySet 替代 LinkedHashSet (有序不重复)
 * CopyOnWriteArrayList.add 与 CopyOnWriteArrayList.addIfAbsent 的区别
 * CopyOnWriteArraySet是借助addIfAbsent方法实现的,由于需要去重,所以性能低于CopyOnWriteArrayList
 */
public class DemoThread28 {
   
   private static volatile int count = 0;
   
   public static void testCOWArrayList() throws Exception{
      CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<Integer>();
      list.add(1);
      list.add(2);
      list.add(3);
      list.add(4);
      System.out.println(list);
      list.add(1); //无论元素是否已经存在都添加
      System.out.println(list);
      //如果元素不存在则不用添加,CopyOnWriteArraySet就是利用此函数实现的
      //英文:Absent adj.   缺席的,不在场的; 缺少的,缺乏的; 不在意的,茫然的;
      list.addIfAbsent(2);
      System.out.println(list);
   }
   
   public static void testCOWArraySet() throws Exception{
      final CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<Integer>();
      set.add(2);
      set.add(3);
      set.add(1);
      set.add(4);
      System.out.println(set);
      set.add(2);
      set.add(3);
      set.add(4);
      System.out.println(set);
   }
   
   public static void main(String[] args) throws Exception {
//    testCOWArrayList();
//    testCOWArraySet();
   }
}

运行testCOWArrayList,结果如下

[1, 2, 3, 4]
[1, 2, 3, 4, 1]
[1, 2, 3, 4, 1]

运行testCOWArraySet,结果如下

[2, 3, 1, 4]
[2, 3, 1, 4]

CopyOnWriteArrayList底层实现

/**
 * Appends the element, if not present.
 *
 * @param e element to be added to this list, if absent
 * @return {@code true} if the element was added
 */
public boolean addIfAbsent(E e) {
    Object[] snapshot = getArray();
    return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
        addIfAbsent(e, snapshot);
}

/**
 * A version of addIfAbsent using the strong hint that given
 * recent snapshot does not contain e.
 */
private boolean addIfAbsent(E e, Object[] snapshot) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] current = getArray();
        int len = current.length;
        if (snapshot != current) {
            // Optimize for lost race to another addXXX operation
            int common = Math.min(snapshot.length, len);
            //找到了就不添加
            for (int i = 0; i < common; i++)
                if (current[i] != snapshot[i] && eq(e, current[i]))
                    return false;
            if (indexOf(e, current, common, len) >= 0)
                    return false;
        }
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

采用了读写分离

CopyOnWriteArraySet底层实现

基于CopyOnWriteArrayList,没有新东西

private final CopyOnWriteArrayList<E> al;
/**
 * Adds the specified element to this set if it is not already present.
 * More formally, adds the specified element {@code e} to this set if
 * the set contains no element {@code e2} such that
 * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
 * If this set already contains the element, the call leaves the set
 * unchanged and returns {@code false}.
 *
 * @param e element to be added to this set
 * @return {@code true} if this set did not already contain the specified
 *         element
 */
public boolean add(E e) {
    return al.addIfAbsent(e);
}

COW容器的弱一致性

使用COW容器的iterator方法实际返回的是COWlterator实例,遍历的数据为快照数据,其他线程对于容器元素增加、删除、修改不对快照产生影响。
对java.util.concurrent.CopyOnWriteArrayList、java.util.concurrent.CopyOnWriteArraySet均适用。

CopyOnWriteArrayList

/**
 * Returns an iterator over the elements in this list in proper sequence.
 *
 * <p>The returned iterator provides a snapshot of the state of the list
 * when the iterator was constructed. No synchronization is needed while
 * traversing the iterator. The iterator does <em>NOT</em> support the
 * {@code remove} method.
 *
 * @return an iterator over the elements in this list in proper sequence
 */
public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
}
/**
 * Gets the array.  Non-private so as to also be accessible
 * from CopyOnWriteArraySet class.
 */
final Object[] getArray() {
    return array;
}
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;

继承ListIterator

tatic final class COWIterator<E> implements ListIterator<E> {
    /** Snapshot of the array */
    //快照
    private final Object[] snapshot;
    /** Index of element to be returned by subsequent call to next.  */
    //指针
    private int cursor;

    private COWIterator(Object[] elements, int initialCursor) {
        cursor = initialCursor;
        snapshot = elements;
    }

    public boolean hasNext() {
        return cursor < snapshot.length;
    }

    public boolean hasPrevious() {
        return cursor > 0;
    }

    //通过指针寻位返回对应数据
    @SuppressWarnings("unchecked")
    public E next() {
        if (! hasNext())
            throw new NoSuchElementException();
        return (E) snapshot[cursor++];
    }

    @SuppressWarnings("unchecked")
    public E previous() {
        if (! hasPrevious())
            throw new NoSuchElementException();
        return (E) snapshot[--cursor];
    }

    public int nextIndex() {
        return cursor;
    }

    public int previousIndex() {
        return cursor-1;
    }

    /**
     * Not supported. Always throws UnsupportedOperationException.
     * @throws UnsupportedOperationException always; {@code remove}
     *         is not supported by this iterator.
     */
    public void remove() {
        throw new UnsupportedOperationException();
    }

    /**
     * Not supported. Always throws UnsupportedOperationException.
     * @throws UnsupportedOperationException always; {@code set}
     *         is not supported by this iterator.
     */
    public void set(E e) {
        throw new UnsupportedOperationException();
    }

    /**
     * Not supported. Always throws UnsupportedOperationException.
     * @throws UnsupportedOperationException always; {@code add}
     *         is not supported by this iterator.
     */
    public void add(E e) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        Object[] elements = snapshot;
        final int size = elements.length;
        for (int i = cursor; i < size; i++) {
            @SuppressWarnings("unchecked") E e = (E) elements[i];
            action.accept(e);
        }
        cursor = size;
    }
}

例子

public class SampleDemo {

    public static void main(String[] args) {
        // 用一个小例子来说明快照的产生
        Object[] array = new Object[]{1,2,3};
        Object[] snapshot = array;
        Object[] newArray = new Object[]{1,2,3,4};
        array = newArray;
        System.out.println("array:"+Arrays.toString(array));
        System.out.println("snapshot:"+Arrays.toString(snapshot));
    }

}

模仿COW容器

array:[1, 2, 3, 4]
snapshot:[1, 2, 3]

COW容器证明读取的是快照,由于读写分离,读取的还是旧版本

public class COWDemo0 {

    public static void main(String[] args) throws InterruptedException {

        CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        Iterator<Integer> iterator = list.iterator();

        Thread td = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add(4);
                list.add(5);
            }
        });

        td.start();
        //线程一定会执行
        td.join();

        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

        System.out.println("------------------");

        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }

    }

}

执行结果

1
2
3
------------------
1
2
3
4
5

Iterator<Integer> iterator = list.iterator();放在td.join();之后,执行的结果如下,对迭代器获取后立刻使用,对时效性要求高则使用for循环

1
2
3
4
5
------------------
1
2
3
4
5

查看源码COWIterator,在构造函数中复制了快照

private COWIterator(Object[] elements, int initialCursor) {
    cursor = initialCursor;
    snapshot = elements;
}

获取快照的时候调用

/**
 * Returns an iterator over the elements in this list in proper sequence.
 *
 * <p>The returned iterator provides a snapshot of the state of the list
 * when the iterator was constructed. No synchronization is needed while
 * traversing the iterator. The iterator does <em>NOT</em> support the
 * {@code remove} method.
 *
 * @return an iterator over the elements in this list in proper sequence
 */
public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
}

而get是直接获取了,用的并不是快照

/**
 * {@inheritDoc}
 *
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
    return get(getArray(), index);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值