文章目录
- 线程安全问题
- 分析
- 常见的线程安全类
- 线程安全类方法组合
- 不可变类型
线程安全问题
分析
局部变量是安全的,成员变量并不安全。
切记,方法是拷贝执行的,方法内部的局部的变量并不共享,所以是安全的,但如果方法调用成员变量,成员变量在堆中,操作的是同一个,这就会有安全问题。
也就是说,一个变量只被一个线程调用,是安全的。
list不安全的(成员):
class ThreadUnsafe {ArrayList<String> list = new ArrayList<>();public void method1(int loopNumber) {for (int i = 0; i < loopNumber; i++) {// { 临界区, 会产生竞态条件method2();method3();// } 临界区}}private void method2() {list.add("1");}private void method3() {list.remove(0);}
}
list安全的(局部):
class ThreadSafe {public final void method1(int loopNumber) {ArrayList<String> list = new ArrayList<>();for (int i = 0; i < loopNumber; i++) {method2(list);method3(list);}}private void method2(ArrayList<String> list) {list.add("1");}private void method3(ArrayList<String> list) {list.remove(0);}
}
不过不是绝对的,如果子类继承并重写的方法3,那么局部变量被暴露给了其他线程,就会出现安全问题。
所以private和final的作用的体现出来了。
class ThreadSafe {public final void method1(int loopNumber) {ArrayList<String> list = new ArrayList<>();for (int i = 0; i < loopNumber; i++) {method2(list);method3(list);}}private void method2(ArrayList<String> list) {list.add("1");}private void method3(ArrayList<String> list) {list.remove(0);}
}class ThreadSafeSubClass extends ThreadSafe{@Overridepublic void method3(ArrayList<String> list) {new Thread(() -> {list.remove(0);}).start();}
}
常见的线程安全类
- String
- Integer
- StringBufffer
- Random
- Vector
- Hashtable
多个线程调用同一实例下,这些类是安全的,因为其方法都是原子性的。
不过多个方法组合并不安全。
线程安全类方法组合
并不安全
例如:
Hashtable table = new Hashtable();
// 线程1,线程2
if( table.get("key") == null) {table.put("key", value);
}
很明显,两个线程如果都读取完null切换线程,那么就会put。
不可变类型
像String,Integer是不可变的,肯定是安全的。
他内部方法源码都是创建一个新对象返回,所以看起来像我们修改了一样。
例如这个subString方法,如果不是截取全部,返回一个新对象。
所以那么加final的类一定安全么?
显然是不一定的,final对应引用类型只是保证其指向不变,但其内部的属性还是可以改变的,所以不安全。