一、变量的线程安全分析
1.1 成员变量和静态变量是否线程安全
- 如果它们没有共享,则线程安全
- 如果它们被共享了,根据它们的状态是否能够改变,有份两种情况
- 如果只有读操作,则线程安全
- 如果有读写操作,则这段代码是临界区,需要考虑线程安全
1.2 局部变量是否线程安全
- 局部变量是线程安全的
- 但局部变量引用的对象则未必
- 如果该对象没有逃离方法的作用访问,他是线程安全的
- 如果该对象逃离方法的作用范围,则需要考虑线程安全。
1.3 局部变量是线程安全的
public static void test1(){
int i=10; //i是局部变量,线程安全的
i++;
}
1.4 成员变量线程不安全
class ThreadUnsafe{
ArrayList<String> list = new ArrayList<>();
//多线程调用method1方法,ArrayList.add和ArrayList.remove的线程不安全。因为都会操作属性size,
// 可能会导致add时size值未加成功,但多次remove
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);}
}
分析:
1.5 将1.4中的成员变量改成局部变量,线程安全
class ThreadSafe{
public 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);}
}
分析:
- list是局部变量,每个线程调用时都会创建其不同实例,没有共享
- 而method2的参数是从method1中传递过来的,与method1中引用同一个对象
二、常见线程安全类
2.1 常见线程安全类
- String
- Integer
- StringBuffer
- Random
- Vector
- Hashtable
- java.util.concurrent包下的类
这里说的线程安全是指:多个线程调用它们同一个实例的某一个方法时,是线程安全的。也可以理解为
- 它们的每个方法是原子的
- 但注意它们多个方法的组合不是原子的。
如下线程安全类方法的组合是线程不安全的
2.2 不可变线程安全性
String、Integer等都是不可变类,因为其内部的属性不可以改变,因此它们的方法都是线程安全的。
三、实例分析
3.1 实例1
class MyServlet extends HttpServlet {
//线程不安全
Map<String,String> map = new HashMap<>();
//线程安全
String s1="...";
//线程安全
final String s2="...";
//线程不安全
Date d1 = new Date();
//线程不安全
final Date d2 = new Date();
public void doGet(HttpServletRequest request, HttpServletResponse response){
//使用上述变量
}
}
3.2 实例2
public class MyServlet extends HttpServlet {
//线程不安全
private UserService userService = new UserServiceImpl();
public void doGet(HttpServletRequest request, HttpServletResponse response){
userService.update();
}
}
public class UserServiceImpl implements UserService{
//记录调用次数
private int count=0;
public void update(){
//...
count++;
}
}