java5同步集合类的应用

一、包装线程不安全的集合

ArrayListLinkedListHashSetTreeSetHashMapTreeMap等都是线程不安全的。当多个并发线程向这些集合中存、取元素时,就可能破坏这些集合的数据完整性。

如果程序中有多个线程可能访问以上这些集合,就可以使用Collections提供的类方法把这些集合包装成线程安全的集合。

返回值 方法名 描述
static Collection synchronizedCollection(Collection c) 返回指定 collection 支持的同步(线程安全的)collection。
static List synchronizedList(List list) 返回指定列表支持的同步(线程安全的)列表。
static Map synchronizedMap(Map m) 返回由指定映射支持的同步(线程安全的)映射
static Set synchronizedSet(Set s) 返回指定 set 支持的同步(线程安全的)set
static SortedMap synchronizedSortedMap(SortedMap m) 返回指定有序映射支持的同步(线程安全的)有序映射。

如果像将传统的集合变成安全的,可以到Collections中找对应的静态方法。

主要使用代理的方式实现安全(具体可以查看源码),通过一个内部类。

private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize
        //所有的方法都需要使用到mutex。 所以在多线程的情况下,使用
        //这种方式去进行同步效率还是比较低的。

        SynchronizedMap(Map m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

        SynchronizedMap(Map m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }

        public int size() {
            synchronized (mutex) {return m.size();}
        }
        ……
 }

但是,这些集合通常效率不是很高。


二、线程安全的集合类

从java5 开始,在java.util.concurrent包下,提供了大量支持高效并发访问的集合接口和实现类

这些线程安全的集合类可大致分为两类。以Concurrent开头的集合类,和 以CopyOnWrite开头的集合类。
Concurrent开头的集合类采用了更加复杂的算法来保证永远不会锁住整个集合,因此在并发写入时有较好的性能

在默认情况下,ConcurrentHashMap支持16个线程并发写入,当超过16个线程并发向该Map写入时,可能有一些线程需要等待,实际上,可通过concurrencyLevel构造参数来支持更多并发写入线程。

与HashMap等普通集合不同,ConcurrentLinkedQueueConcurrentHashMap支持多线程并发访问,所以当使用迭代器来遍历集合元素时,该迭代器可能不能反映出创建迭代器之后所做的修改,但程序不会抛出任何异常。


CopyOnWriteArrayList集合。它采用了复制底层数组的方式来实现写操作。由于执行写入操作时需要频繁地复制数组,性能比较差。因此CopyOnWriteArrayList适合在读取操作远大于写入操作的场景,例如缓存等。


部分工具类描述

描述
ConcurrentHashMap 一个高效的并发HashMap
CopyOnWriteArrayList 在读多写少的场合,这个List性能非常好,远远好于Vector
ConcurrentLinkedQueue 高效的并发队列,使用链表实现。 可以看做一个线程安全的LinkedList
BlockiingQueue 是一个接口,非常适合用于做数据共享的通道
ConcurrentSkipListMap 跳表的实现。是一个Map,使用跳表的数据结构进行快速查找

三、案例

java5同步集合类的应用_第1张图片


每个段都有独立的锁。实现了并行,提高了效率; 在1.8以后又采用 了CAS算法去实现同步


3.1 java.util.Collections包中的并发类

使用Collections包中的并发类,去实现同步,但是多线程并发执行修改数据,抛出异常:并发修改异常

package test;

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


public class TestCopyOnWriteArrayList {

    public static void main(String[] args) {
        HelloThread ht = new HelloThread();

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

}

class HelloThread implements Runnable{

    private static List list = Collections.synchronizedList(new ArrayList());


    static{
        list.add("AA");
        list.add("BB");
        list.add("CC");
    }

    @Override
    public void run() {

        Iterator it = list.iterator();

        while(it.hasNext()){
            System.out.println(it.next());

            list.add("AA");
        }

    }

}

运行结果

java5同步集合类的应用_第2张图片


3.2 java.util.concurrent包中的并发类

CopyOnWriteArrayList/CopyOnWriteArraySet : 写入并复制

注意: 添加操作多时,效率低,因为每次添加时都会进行复制,开销非常的大。并发迭代操作多时可以选择

package test;


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


public class TestCopyOnWriteArrayList {

    public static void main(String[] args) {
        HelloThread ht = new HelloThread();

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

}

class HelloThread implements Runnable{

    private static CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();


    static{
        list.add("AA");
        list.add("BB");
        list.add("CC");
    }

    @Override
    public void run() {

        Iterator it = list.iterator();

        while(it.hasNext()){
            System.out.println(it.next());

            list.add("AA");
        }

    }

}

运行结果正常。


参考

软件包 java.util.concurrent 的描述 http://tool.oschina.net/apidocs/apidoc?api=jdk-zh

你可能感兴趣的:(java多线程)