Java-JUC(四):同步容器介绍

同步容器简介

针对容器我们知道有HashMap,HashTable,其中HashMap是一个非线程安全的,HashMap在并发执行put操作时会引起死循环,导致CPU利用率接近100%。因为多线程会导致HashMap的Node链表形成环形数据结构,一旦形成环形数据结构,Node的next节点永远不为空,就会在获取Node时产生死循环,而且死循环发生在发生在扩容时

HashTable是线程安全的,但是在复合操作上会线程不安全的现象。

备注:

1)什么是复合操作?比如添加元素,需先判断是否元素存在,元素不存在时才添加。

2)hashtable是在插入和删除上都是索表的方式,因此效率特别低。

在jdk1.5之后提供java.util.concurrent包下的一些同步容器:java.util.concurrent.ConcurrentHashMap<K, V>。

ConcurrentHashMap性能讲解:

ConcurrentHashMap的性能要比HashTable性能高,而且高很多。

1)ConcurrentHashMap在jdk1.7之前采用了“锁分段”机制,默认把一个ConcurrentHashMap分为16个segment,每个segment下默认也是长度为16的哈希表,在每个哈希元素下是链表。这样就可默认每段有一个锁,一次可以最大并发操作16。

《Java-JUC(四):同步容器介绍》

2)在jdk1.8之后ConcurrentHashMap采用CAS算法,可以把CAS算法错略的认为是无锁算法。

3)java.util.concurrent包还提供了设计用于多线程上下文中的Collection实现:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList和CopyOnWriteArraySet。

4)当希望许多线程访问一个给定的Collection时

  a)ConcurrentHashMap由于同步的HashMap;

  b)ConcurrentSkipListMap通常优于同步的TreeMap;

5)当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList优于同步的ArrayList。

CopyOnWriteArrayList的用法示例:

测试ArrayList并发读写:

package com.dx.juc.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CopyOnWriteTest {
    public static void main(String[] args) {
        new Thread(new MyCopyOnWrite()).start();
    }
}

class MyCopyOnWrite implements Runnable {
    private static List<String> items = Collections.synchronizedList(new ArrayList<String>());

    static {
        items.add("A");
        items.add("B");
        items.add("C");
    }

    public void run() {
        for (String item : items) {
            System.out.println(item);
            items.add("D");
        }
    }

}

在执行时,抛出异常:

A
Exception in thread "Thread-0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
    at java.util.ArrayList$Itr.next(ArrayList.java:831)
    at com.dx.juc.test.MyCopyOnWrite.run(CopyOnWriteTest.java:23)
    at java.lang.Thread.run(Thread.java:745)

如果使用CopyOnWriteArrayList时,并不会抛出异常,原理是每次在写入时,会在底层拷贝一份新的数据,因此如果是写入时性能不高。

package com.dx.juc.test;

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteTest {
    public static void main(String[] args) {
        MyCopyOnWrite myCopyOnWrite = new MyCopyOnWrite();

        for (int i = 0; i < 5; i++) {
            new Thread(myCopyOnWrite).start();
        }
    }
}

class MyCopyOnWrite implements Runnable {
    private static CopyOnWriteArrayList<String> items = new CopyOnWriteArrayList<String>();

    static {
        items.add("A");
        items.add("B");
        items.add("C");
    }

    public void run() {
        System.out.println(Thread.currentThread().getName() + ":start");
        Iterator<String> iterator = items.iterator();
        while (iterator.hasNext()) {
            System.out.println(Thread.currentThread().getName() + ":" + iterator.next());
            items.add("D");
        }
        System.out.println(Thread.currentThread().getName() + ":end");
    }

}

 

    原文作者:cctext
    原文地址: https://www.cnblogs.com/yy3b2007com/p/8910740.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞