成员变量和静态变量是否线程安全
- 很明显如果它们没有被共享,则线程安全
- 如果它们被共享了,根据他们的状态是否能够改变,又分为如下两种情况
- 如果只有读操作,线程安全
- 如果有写操作,使用它们的代码处于临界区,线程不安全
局部变量引用线程安全情况示例:
//=========================成员变量线程不安全分析==============================
static class MemberVariableUnsafe {
List<String> list = new ArrayList<String>();
public void method1(int loopNum) {
for (int i = 0; i < loopNum; i++) {
method2();
method3();
}
}
public void method2() {
list.add("222");
}
public void method3() {
list.remove(0);
}
}
public static void main(String[] args) {
MemberVariableUnsafe memberVariableUnsafe = new MemberVariableUnsafe();
log.info("=========================成员变量线程不安全分析==============================");
for (int i = 0; i < 2; i++) {
new Thread(() -> {
memberVariableUnsafe.method1(50);
}, "Thread-" + i).start();
}
}
//执行结果(多执行几次,一定会出现以下报错)
21:19:45.145 [main] INFO com.leolee.multithreadProgramming.concurrent.threadSafetyAnalyses.ThreadSafetyTest - =========================成员变量线程不安全分析==============================
Exception in thread "Thread-0" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.remove(ArrayList.java:492)
at com.leolee.multithreadProgramming.concurrent.threadSafetyAnalyses.ThreadSafetyTest$MemberVariableUnsafe.method3(ThreadSafetyTest.java:51)
at com.leolee.multithreadProgramming.concurrent.threadSafetyAnalyses.ThreadSafetyTest$MemberVariableUnsafe.method1(ThreadSafetyTest.java:42)
at com.leolee.multithreadProgramming.concurrent.threadSafetyAnalyses.ThreadSafetyTest.lambda$main$0(ThreadSafetyTest.java:60)
//修改list为局部变量即可
public void method1(int loopNum) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < loopNum; i++) {
method2(list);
method3(list);
}
}
public void method2(List<String> list) {
list.add("222");
}
public void method3(List<String> list) {
list.remove(0);
}
同一个对象内部成员变量资源共享,多线程调用method1,实质上都是对memberVariableUnsafe对象的操作,两个线程都是持有了memberVariableUnsafe,所以操作的list也是同一个,没什么好说的。
当list变成局部变量的时候,method2和method3在每一个线程中操作的都是method1新建的list,没有线程安全问题。
局部变量是否线程安全
- 局部变量是线程安全的
- 但是局部变量应用的对象未必安全
- 如果该对象没有逃离方法的作用范围,它是线程安全的
- 如果该对象逃离方法的用作范围,线程可能不安全
分析
局部变量安全:
/*
* 功能描述: <br>
* 〈i是局部变量,每个线程调用方法test1的时候,会在每个线程的栈帧中创建多份,因此不存在线程安全问题〉
* @Param: []
* @Return: void
* @Author: LeoLee
* @Date: 2020/11/30 21:03
*/
public static void test1() {
int i = 10;
i++;
}
这个i是test1方法中的局部变量,但是每一个线程调用test1方法的时候,其实都是在线程自己的栈帧中给i分配了内存,所有的赋值、自增操作都是在自己的栈帧中执行的,所以这里的i是线程安全的
局部变量引用非线程安全情况示例:
//本来局部变量是线程安全的
class LocalVariableSafe {
public void method1(int loopNum) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < loopNum; i++) {
method2(list);
method3(list);
}
}
public void method2(List<String> list) {
list.add("222");
}
public void method3(List<String> list) {
list.remove(0);
}
}
//有一个类继承了LocalVariableSafe
class LocalVariableSubClass extends LocalVariableSafe {
@Override
public void method3(List<String> list) {
new Thread(() -> {
list.remove(0);
}).start();
}
}
public static void main(String[] args) {
log.info("=========================局部变量发生逃离,线程安全分析==============================");
LocalVariableSubClass localVariableSubClass = new LocalVariableSubClass();
for (int i = 0; i < 2; i++) {
new Thread(() -> {
localVariableSubClass.method1(50);
}, "Thread-" + i).start();
}
}
//多次执行会发生如下报错:
21:44:15.772 [main] INFO com.leolee.multithreadProgramming.concurrent.threadSafetyAnalyses.ThreadSafetyTest - =========================局部变量发生逃离,线程安全分析==============================
Exception in thread "Thread-96" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.remove(ArrayList.java:492)
at com.leolee.multithreadProgramming.concurrent.threadSafetyAnalyses.ThreadSafetyTest$LocalVariableSubClass.lambda$method3$0(ThreadSafetyTest.java:80)
at java.lang.Thread.run(Thread.java:745)
规避这种情况就要考虑访问修饰符的使用是否可以是public,方式是否要加final