常用集合线程安全分析

ArrayList在多线程情况下,不安全

具体代码
package com.shaonian.juc.list_thread_secure;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author 长名06
 * @version 1.0
 * 演示ArrayList集合线程不安全
 */
public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                //会出现java.util.ConcurrentModificationException 并发修改异常
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

解决方案

使用Vector/ConcurrentHashMap集合
package com.shaonian.juc.list_thread_secure;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;

/**
 * @author 长名06
 * @version 1.0
 * Vector集合,线程安全 不推荐使用
 */
public class VectorDemo {
    public static void main(String[] args) {
        List<String> list = new Vector<>();

        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

使用Collections中的synchronizedList()/synchronizedMap()

package com.shaonian.juc.list_thread_secure;

import java.util.*;

/**
 * @author 长名06
 * @version 1.0
 * 使用Collections中的synchronizedList方法,转成对应线程安全的集合,不推荐使用
 */
public class CollectionsDemo {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

使用CopyOnwriteArrayList

涉及到写时复制技术,就是说,此集合是可以并发读,但是写操作,同时只能有一个线程执行。写操作时,先将原有的集合内容,copy一份,然后写入新的内容,然后再覆盖原有的集合,覆盖后的集合,就可以读和写了

package com.shaonian.juc.list_thread_secure;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author 长名06
 * @version 1.0
 * 使用线程安全的集合如CopyOnWriteArrayList
 */
public class CopyOnWriteArrayListDemo {
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

CopyOnWriteList

CopyOnWriteList相当于线程安全的ArrayList,和ArrayList一样,它是个可变数组,不过有一下特性

  • 1.它最适合具有以下特征的程序使用,List大小通常保持很小,读操作远多于写操作,需要再遍历期间防止线程间的冲突。
  • 2.线程安全。
  • 3.可变操作,需要复制整个数组,所以可变操作(add(),set(),remove()等)操作开销大。
  • 4.迭代器支持hasNext(),next()等不可变操作,但是不支持remove()等操作。
  • 5.使用迭代器进行遍历的速度会很快,并且不会和其他线程发生冲突,再构造迭代器时,迭代器依赖于不变的数组快照。
    有数组拷贝的操作,会存在一个问题,数据不一致的问题,如果写线程还没来得及写入内存,其他线程就会读到脏数据。但是在CopyOnWriteList中,保存数据的数组,使用volatile关键字修饰,也就是说,当前数组的修改,会被其他线程立刻读取到。保证了一个线程读取volatile修饰的数组,总能看到其他线程对改数组最后的写入,就这样,通过volatile提供了“读取数据总是最新的”这个机制的保证。

HashSet和HashMap在多线程情况下,不安全

package com.shaonian.juc.list_thread_secure;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author 长名06
 * @version 1.0
 */
public class HashSetDemo {
    public static void main(String[] args) {
        //解决方案,使用CopyOnWriteHashSet
        Set<String> set = new HashSet<>();
        for (int i = 0; i < 30; i++) {
            //会出现java.util.ConcurrentModificationException
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}
package com.shaonian.juc.list_thread_secure;

import java.util.*;

/**
 * @author 长名06
 * @version 1.0
 */
public class HashMapDemo {
    public static void main(String[] args) {
        //解决方案,使用ConcurrentHashMap
        Map<String,String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            String key = String.valueOf(i);
            //会出现java.util.ConcurrentModificationException
            new Thread(() -> {
                map.put(key,UUID.randomUUID().toString().substring(0, 8));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}

小结

线程不安全与其对应的安全集合

集合中存在线程存在和线程不安全的例如ArrayList -- Vector HashMap -- HashTable,但是以上都是syncronized关键字实现的。

Collections构建的线程安全集合

通过使用Collections中的synchronizedList()/synchronizedMap(),将不安全的集合,转成线程安全的。

java.util.concurrent包下的

CopyOnWriteList和CopyOnWriteSet类型,通过动态数组与加锁保证线程安全。

只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。下·

热门相关:茅山捉鬼人   我家影后超甜的   我家影后超甜的   我在末世有套房   宴先生缠得要命