概述
初探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线程安全的三种实现方式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复