美文网首页
线程不安全

线程不安全

作者: 码农小钰 | 来源:发表于2020-06-24 09:57 被阅读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

    相关文章

      网友评论

          本文标题:线程不安全

          本文链接:https://www.haomeiwen.com/subject/ipglfktx.html