目录
(2)使用Collections.synchronizedList(List)
(1)使用Collections.synchronizedSet()
(1)使用Collections.synchronizedMap()
1.ArrayList线程不安全分析
1.1 故障现象
示例代码:
package CollectionTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ArrayListDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
当多个线程操纵同一个集合ArrayList的时候,抛出了java.util.ConcurrentModificationException
1.2 导致原因分析
- ArrayList底层的所有方法,均未有任何机制来保证线程同步,所以当多个线程进行写操作如add、set的时候会出现并发修改的异常
1.3 解决方案
(1)使用Vector
package CollectionTest;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
public class ArrayListDemo {
public static void main(String[] args) {
List<String> list = new Vector<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
(2)使用Collections.synchronizedList(List)
package CollectionTest;
import java.util.*;
public class ArrayListDemo {
public static void main(String[] args) {
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
(3)使用CopyOnWriteArrayList
package CollectionTest;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class ArrayListDemo {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
1.4 优化建议
- Vector的方法均使用synchronized修饰,单线程执行,在高并发下效率比较低,不建议使用
- 关于Collections.SynchronizedList和CopyOnWrite的详细使用分析和建议见后续博客分析
2.HashSet线程不安全分析
2.1 故障分析
示例代码:
package CollectionTest;
import java.util.*;
public class HashSetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
2.2 解决方案
(1)使用Collections.synchronizedSet()
package CollectionTest;
import java.util.*;
public class HashSetDemo {
public static void main(String[] args) {
Set<String> set = Collections.synchronizedSet(new HashSet<>());
for (int i = 0; i < 100; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
(2)使用CopyOnWriteArraySet
package CollectionTest;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
public class HashSetDemo {
public static void main(String[] args) {
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
3.HashMap线程不安全分析
3.1 故障现象
package CollectionTest;
import java.util.*;
public class HashMapDemo {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
3.2 解决方案
(1)使用Collections.synchronizedMap()
package CollectionTest;
import java.util.*;
public class HashMapDemo {
public static void main(String[] args) {
Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
for (int i = 0; i < 100; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
(2)使用ConcurrentHashMap
package CollectionTest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class HashMapDemo {
public static void main(String[] args) {
Map<String,String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
总结:
- 可以发现ArrayList、HashSet、HashMap均是线程不安全的,因为在它们的实现中没有任何同步机制来保证线程同步
- 它们均可以使用Collections.SynchronizedXXX方法来进行同步
- 从JDK1.5开始,java.util.concurrent包下提供了一些并发集合供多线程场景下使用,后续博客会深入分析这些集合