线程不安全

2020-06-24  本文已影响0人  码农小钰

List中的ArrayList(),set中Hashset,Map中得HashMap.
当如下时主函数main为单线程操作list,list底层是一个Object得数组

public class Notsafe{
    public static void main(String[] args){
          List<String> list = new ArrayList<>();
             list.add("a");
             list.add("b");
             list.add("c");
             list.forEach(System.out::println);
    }
}

结果如下图:


image.png

模拟多线程操作list资源

public class NotSafeDemo
{
    public static void main(String[] args)
    {
        List<String> list = new ArrayList<>();

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

则会出现如下图报错(并发修改异常)


image.png

如何解决呢?有三种方法解决上面出现得问题:
1.使用List中实现类Vector接口.这样使用Vector接口把整个list都加锁数据得一致性高度一致.

public class Test {
    public static void main(String[] args){
        List<String> list = new Vector<>();
        for (int i = 1; i <=30; i++)
        {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

2.使用Collections工具类给list加上同步锁

3.使用写时复制(CopyOnWriteArrayList) ,推荐使用这种方法.具体代码如下,

public class NotSafeDemo
{
    public static void main(String[] args)
    {
        List<String> list = new CopyOnWriteArrayList<>();

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

再来看看源码具体是怎么实现得
每次添加得时候复制原来得数组然后再重新定义一个新的数组,数组长度是原来长度加一,添加完之后再把旧的删除然后每次新增拿到的都是最新的数组

/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
image.png

set的使用,使用Hashset时如果时多线程操作则会出现并发修改异常和list一样.


image.png

解决方法一样是使用写时复制CopyOnWriteArraySet

public class NotSafeDemo
{
    public static void main(String[] args) {
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <=30; i++)
        {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }

Map中HashMap也是不安全得,所以三者报错得信息都是一样得,都是并发修改异常,而Map得解决方式则是使用ConcurrentHashMap线程安全的实现类.

public class NotSafeDemo
{
    public static void main(String[] args) {
        Map<String,String> map = new ConcurrentHashMap<>();
        for (int i = 1; i <=30; i++)
        {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}
image.png
上一篇下一篇

猜你喜欢

热点阅读