ArrayList,HashSet,HashMap等集合不是线程安全的.在多线程环境中使用这些集合可能会出现数据不一致的情况,或者产生异常。
Vector集合,HashTable集合是线程安全,在这些集合中使用synchronized关键字把方法修饰为同步方法,只允许由一个线程调用其中一个方法, 所以又把Vector,HashTable集合称为同步集合。
在Collections工具类中提供了一组synchronizedXXX()方法可以把不是线程安全的集合转换为线程安全的集合,这也是同步集合。
package com.wkcto.syncColleciton;
import java.util.ArrayList;
import java.util.Vector;
/**
* 并发下的ArrayList集合
*/
public class Test01 {
//创建ArrayList集合
// private static ArrayList<Integer> arrayList = new ArrayList<>();
//创建Vector集合
private static Vector<Integer> arrayList = new Vector<>();
//定义线程类,在该线程中不断的向ArrayList集合中添加元素
private static class AddDataThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
arrayList.add( i );
}
}
}
public static void main(String[] args) throws InterruptedException {
//启动三个线程,向ArrayList集合中添加数据
AddDataThread t1 = new AddDataThread();
AddDataThread t2 = new AddDataThread();
AddDataThread t3 = new AddDataThread();
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
System.out.println( arrayList.size() );
/*
当程序运行后,会产生异常Exception in thread "Thread-2" Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: Index 48 out of bounds for length 15
ArrayList底层是使用数组来存储数据的,在向ArrayList集合中添加元素时,如果元素的数量超过底层数组的长度,数组需要扩容, 在扩容过程中,没有对数组进行保护,在多线程环境中可能会出现一致性被破坏的情况,一个线程在扩容,另外一个线程在存储数据,访问了不一致的内部状态,导致了数组的越界
还有一个可能出现的问题,最终集合的容量可能不准确,这也是多线程访问冲突造成的
解决方法:
在List集合中还有实现类是Vector,它的底层也是数组,它是线程安全的,把ArrayList换成Vector即可
Vector中的操作都使用了synchronized关键字把方法修饰为同步方法, 即在多线程环境 中,只能有某一个线程调用它的某个方法, 所以 也称Vector集合为同步集合
*/
}
}
package com.wkcto.syncColleciton;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
/**
* HashMap不是线程安全的
*/
public class Test02 {
//创建HashMap集合,存储<整数,整数的二进制形式>
// private static Map<String,String> map = new HashMap<>();
//使用线程安全的HashTable存储
private static Map<String,String> map = new Hashtable<>();
//定义线程类,向map中添加数据
private static class AddDataThread extends Thread{
private int start = 0 ;
public AddDataThread(int start) {
this.start = start;
}
@Override
public void run() {
//通过循环把奇数与偶数分开
for (int i = start; i < start+ 100000; i+=2) {
//把整数,整数的二进制添加到map中
map.put(Integer.toString(i), Integer.toBinaryString(i));
}
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程把从0开始的偶数及对应的二进制添加到map中
AddDataThread t1 = new AddDataThread(0);
//创建线程把从1开始的奇数及对应的二进制添加到map中
AddDataThread t2 = new AddDataThread(1);
t1.start();
t2.start();
t1.join();
t2.join();
//两个线程添加完成后,查看map中键值对的数量
System.out.println(map.size());
/*
运行程序,输出map的键值对数量,它可能是一个小于100000的数字,即出现了数据不一致的线程安全问题
解决方法:
可以把HashMap集合换成HashTable
在HashTable集合中,使用synchronized关键字把操作修饰为同步方法, 即多线程环境中只允许由一个线程来操作HashTable集合,它是线程安全的, 也把HashTable称为同步集合
*/
}
}