线程不安全类
什么是线程不安全的类?
简单来说,如果一个类对象同时可以被多个线程访问,如果不做处理,容易表现出线程不安全现象。
1.StringBuilder和StringBuffer
StringBuilder是线程不安全的
StringBuffer是线程安全的
因为StringBuffer的源码中,基本所有的方法上都加了synchronized关键字;而StringBuilder并不是。
但是正因为StringBuffer用了synchronized,所以同一时间只能有一条线程访问,造成效率低,性能上有损耗。
所以如果不考虑多线程访问情况下,大多用StringBuilder来实现。因为在局部变量使用中,线程封闭,不涉及线程问题。
这就是JAVA为什么提供了两个字符串拼接类的原因。
2.SimpleDateFormat和JodaTime包
SimpleDateFormat是日期转换的类,是线程不安全的。因为在多线程情况下运行,直接抛异常。
如果想使用这个类的话,要注意,不要声明在全局变量中,可以声明在方法中,局部变量,不涉及线程问题就可以。
JodaTime包本质上不是Java提供的,是一个单独的包。也是一个日期转换的,这个包里面的类是线程安全的。
包的依赖如下:
3.ArrayList,HashSet,Hashmap等Collections
ArrayList,HashSet,Hashmap都是线程不安全的。
如何处理能将上面的几个类转换为线程安全的呢?
1.使用同步容器(也不是线程完全安全的,完全安全的话需要使用并发容器,所以基本没人使用这个了)
①.直接用对应的线程安全类
ArrayList --> Vector,Stack
Hashmap --> HashTable
②.用Collections.synchronizedXXX方法实现转换 (List,Set,Map)
Vector类实现了List接口,它的方法都使用了synchronized关键字来修饰
Stack类继承了Vector类,就是咱们数据结构中的栈。它的方法都使用了synchronized来修饰
HashTable的方法都使用了synchronized关键字来修饰,但是它的key,value不能为NULL,需注意。
Vector类,迭代器遍历的时候,如果对其进行了增删操作,会出现并发修改异常。普通for循环不会。
举例:
public static Object getLast(Vector list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
public static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
虽然上面的方法看起来没有问题,Vector自身的方法也是同步的,但是在多线程环境中还是隐藏着问题。如果有两个线程A,B同时调用上面的两个方法,假设list的大小为10,这里计算得到的lastIndex为9,线程B首先执行了删除操作(多线程之间操作执行的不确定性导致),而后线程A调用了list.get方法,这时就会发生数组越界异常。导致问题的原因就是上面的复合操作不是原子操作,这里可以通过在方法内部使用list对象锁来实现原子操作。
2.使用并发容器
(单独一个笔记写 :线程安全-并发容器)
线程不安全的写法
1.先检查后执行的写法
if ( ) { }