文章目录
- 不安全的集合
- Java中提供的安全措施
- JUC下的安全List集合
- 性能方面
不安全的集合
在单线程应用中,通常采取new ArrayList()
,指定一个List集合,用于存放可重复的数据。
但在多线程下,往往会出现意想不到的问题,代码如下所示:
import java.util.*;
public class ListTest {
public static void main(String[] args) throws InterruptedException {
// 创建list集合
//List<String> lists = Arrays.asList("1", "2", "3");
// 不安全
List<String> lists = new ArrayList<>();
// 开启十个线程增加数据
for (int i = 1; i <= 40; i++) {
new Thread(()->{
lists.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(Thread.currentThread().getName()+"=="+lists);
},String.valueOf(i)).start();
}
}
}
其运行结果如下所示:
多线程操作同一集合对象信息,往往会出现java.util.ConcurrentModificationException
异常报错信息。
Java中提供的安全措施
在java语言中,提供了一种新的List集合,java.util.Vector
类,具体看下列代码:
import java.util.*;
public class ListTest {
public static void main(String[] args) throws InterruptedException {
// 创建list集合
//List<String> lists = Arrays.asList("1", "2", "3");
// 不安全
//List<String> lists = new ArrayList<>();
List<String> lists = new Vector<>();
// 开启十个线程增加数据
for (int i = 1; i <= 40; i++) {
new Thread(()->{
lists.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(Thread.currentThread().getName()+"=="+lists);
},String.valueOf(i)).start();
}
}
}
运行日志如下所示:
不会出现java.util.ConcurrentModificationException
报错信息。为什么能保证数据的安全操作?
采取了
synchronized
针对方法执行调用者加锁,保证add操作的多线程安全性!
JUC下的安全List集合
在JUC包下,提供有以下几种创建安全集合的方式。
- 方式一:Collections.synchronizedList(new ArrayList<>());
import java.util.*;
public class ListTest {
public static void main(String[] args) throws InterruptedException {
List<String> lists = Collections.synchronizedList(new ArrayList<>());
// 开启十个线程增加数据
for (int i = 1; i <= 40; i++) {
new Thread(()->{
lists.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(Thread.currentThread().getName()+"=="+lists);
},String.valueOf(i)).start();
}
}
}
查看底层源码实现逻辑
判断传入的 list 集合类型,判断类型是否为
java.util.RandomAccess
,如果是则采取java.util.Collections.SynchronizedRandomAccessList
构造集合,如果不是则采取java.util.Collections.SynchronizedList
构造集合。
源码中对应的add操作逻辑如下所示:
采取
synchronized
同步代码块的方式,对数据的add操作实现加锁!
- 方式二:new CopyOnWriteArrayList();
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class ListTest {
public static void main(String[] args) throws InterruptedException {
List<String> lists = new CopyOnWriteArrayList<>();
// 开启十个线程增加数据
for (int i = 1; i <= 40; i++) {
new Thread(()->{
lists.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(Thread.currentThread().getName()+"=="+lists);
},String.valueOf(i)).start();
}
}
}
源码中的介绍如下:
显而易见,其逻辑如下所示:
1、调用add方法后,拿到
java.util.concurrent.locks.ReentrantLock
对象信息。
2、调用lock.lock()
拿到锁!
3、将原数组对象copy操作,并创建原数组大小+1的新数组。
4、将新数据放入新数组中。
5、任何操作finally,都进行锁的释放!
性能方面
JUC包下的Lock操作,都比synchronized性能更好!