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
网友评论