我是靠谱客的博主 踏实钢铁侠,最近开发中收集的这篇文章主要介绍JAVA多线程之初探List线程安全的三种实现方式,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

初探List线程安全的三种实现方式

先演示一下List线程不安全的情况

package JUC.unsafe;

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

/**
 * ArrayList线程不安全演示
 * ConcurrentModificationException:并发操作异常
 */
public class ListUnSafe {
    public static void main(String[] args) {
        //线程安全展示
        Safe safe = new Safe();
        safe.safe();
        //线程不安全展示
//        Unsafe unsafe = new Unsafe();
//        unsafe.unsafe();
    }
}
//安全演示
class Safe{
    /**
     * 解决方案:
     *  1、List<String> list = new Vector();
     *      使用Vector()保证线程安全
     *  2、List<String> list = Collections.synchronizedList(new ArrayList<>);
     *      使用Collections集合类将list转换为线程安全的list
     *  3、List<String> list = new CopyOnWriteArrayList<>();
     *      使用concurrent包下的CopyOnWriteArrayList类
     */
    List<String> list = new CopyOnWriteArrayList<>();
    public void safe(){
        for (int i = 0; i <10 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}
//不安全演示类
class Unsafe{
    List<String> list = new ArrayList();
    public void unsafe(){
        for (int i = 0; i <10 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

源码追踪三种解决方式

Vector()方式

public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

通过源码可以看到Vector类是通过在方法上加synchronized锁实现线程同步。但是大家都知道synchronized效率较低

Collections.synchronizedList()方式

public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }
  • 进入synchronizedList()方法可以看到return 一个三元表达式。判断参数是ArrayList还是LinkedList
  • tip:RandomAccess是一个接口,用于标识对象支持随机访问
  • 此处传入ArrayList示范,进入SynchronizedRandomAccessList方法
static class SynchronizedRandomAccessList<E>
        extends SynchronizedList<E>
        implements RandomAccess {

        SynchronizedRandomAccessList(List<E> list) {
            super(list);
        }
	//其余方法省略...
    }
  • 根据源码可以看到SynchronizedRandomAccessList方法是Collections类的静态内部类SynchronizedRandomAccessList的构造方法

  • SynchronizedRandomAccessList()方法调用了父类的构造方法

static class SynchronizedList<E>
        extends SynchronizedCollection<E>
        implements List<E> {
        private static final long serialVersionUID = -7754090372962971524L;

        final List<E> list;

        SynchronizedList(List<E> list) {
            super(list);
            this.list = list;
        }
    //其余方法省略...
}
  • 可以看到继续调用父类构造方法
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 3053995032091335093L;

        final Collection<E> c;  // Backing Collection
        final Object mutex;     // Object on which to synchronize

        SynchronizedCollection(Collection<E> c) {
            this.c = Objects.requireNonNull(c);
            mutex = this;
        }
}
  • Objects.requireNonNull方法判断传入的集合是否为空,为空抛出NullPointerException()异常,否则返回集合本身

  • 将对象本身赋值给final Object mutex变量,作为锁对象使用

  • 看到这是不是还不知道到底如何进行的同步呢?我们倒回来看,再探SynchronizedList

    static class SynchronizedList<E>
        extends SynchronizedCollection<E>
        implements List<E> {
        private static final long serialVersionUID = -7754090372962971524L;

        final List<E> list;

        SynchronizedList(List<E> list) {
            super(list);
            this.list = list;
        }
        SynchronizedList(List<E> list, Object mutex) {
            super(list, mutex);
            this.list = list;
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return list.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return list.hashCode();}
        }

        public E get(int index) {
            synchronized (mutex) {return list.get(index);}
        }
        public E set(int index, E element) {
            synchronized (mutex) {return list.set(index, element);}
        }
        public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
        }
        public E remove(int index) {
            synchronized (mutex) {return list.remove(index);}
        }

        public int indexOf(Object o) {
            synchronized (mutex) {return list.indexOf(o);}
        }
        public int lastIndexOf(Object o) {
            synchronized (mutex) {return list.lastIndexOf(o);}
        }

        public boolean addAll(int index, Collection<? extends E> c) {
            synchronized (mutex) {return list.addAll(index, c);}
        }

        public ListIterator<E> listIterator() {
            return list.listIterator(); // Must be manually synched by user
        }

        public ListIterator<E> listIterator(int index) {
            return list.listIterator(index); // Must be manually synched by user
        }

        public List<E> subList(int fromIndex, int toIndex) {
            synchronized (mutex) {
                return new SynchronizedList<>(list.subList(fromIndex, toIndex),
                                            mutex);
            }
        }
		...
    }
  • 发现每一个方法都使用了一个同步代码块,而锁正是之前的mutex对象。由此可以发现Collections.synchronizedList()方法是通过为普通方法加上同步代码块解决同步问题的

CopyOnWriteArrayList()方式

进入CopyOnWriteArrayList()源码看看

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;

    final transient ReentrantLock lock = new ReentrantLock();

    private transient volatile Object[] array;


    final Object[] getArray() {
        return array;
    }


    final void setArray(Object[] a) {
        array = a;
    }
	public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
    ...
}
  • 可以看出创建了一个transient volatile Object[] array;数组对象

进入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();
        }
    }
  • 发现是使用ReentrantLock的方式进行同步控制
  • 并且采用写时复制的方式来保证读写的并发控制
    至此3种线程安全的方式就已经全部分析完毕了。完结撒花

最后

以上就是踏实钢铁侠为你收集整理的JAVA多线程之初探List线程安全的三种实现方式的全部内容,希望文章能够帮你解决JAVA多线程之初探List线程安全的三种实现方式所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(39)

评论列表共有 0 条评论

立即
投稿
返回
顶部