多线程中 静态变量,普通变量(实例变量),局部变量,静态方法,普通方法 的线程安全问题
程序员文章站
2022-03-23 23:21:09
...
多线程中 静态变量,普通变量(实例变量),局部变量,静态方法,普通方法 的线程安全问题
类型 | 是否安全 | 存储位置 | 解释 |
---|---|---|---|
静态变量 | 线程不安全 | 方法区 | 静态变量为类所持有,为所有对象共享,全局只有一份,一旦静态变量被修改,其他对象均对修改可见,所以线程不安全 |
普通变量(实例变量) | 单例模式下不安全 非单例模式下安全 |
堆 | 普通变量为这个对象实例私有,在虚拟机的堆中分配,单例模式下,所有的操作都是针对这一个对象的属性,就会像静态变量那样,一旦被某个线程修改后,对其他的线程都可见,所以非线程安全。如果每个线程执行都是一个新的对象,每个对象间的属性就不会产生影响,所以是线程安全的。 |
局部变量 | 线程安全 | 栈 | 每个线程执行的时候,会把局部变量保存在各自的栈帧的工作内存中,线程之间不共享不可见,所以不存在线程安全问题 |
静态方法 | 引用静态变量不安全 没有引用静态变量安全 |
方法区 | 如果引用了静态变量,情形就如果静态变量的结果(只能存在引用静态变量,静态方法和属性可以被非静态引用,但是静态不能引用非静态,因为静态方法在类加载的时候就初始化了,除了final类型),不引用就是一个普通的方法,只不过加上静态修饰后,在整个JVM中只会保存一份。 |
普通方法 | 是否引用了变量,以及是否单例运行 | 栈 | 单例情况下,如果没有引用内部的变量,线程安全的,非单例的情况下,引不引入变量都是安全的。 |
测试静态变量
所有的线程操作一个变量,线程之间不安全
public class StaticTest implements Runnable {
// 此处定义一个静态变量
private static int i;
/**
* 在线程运行的时候,如果正常的情况下应该输出,2,10
* 如果证明线程不安全,即其他线程修改了别的线程正常运行的值
* 会出现A线程把i修改为5,即将执行i*2=10的时候,其他线程B把i修改为2,使结果输出为4
* A线程把i赋值为2,即将执行输出i=2的时候,线程B把i修改为5,使结果输出为5
* 无论输出4,5都是线程不安全的体现
*/
@Override
public void run() {
i=2;
System.out.println("["+Thread.currentThread().getName()+"] 当前静态变量i="+i);
i=5;
System.out.println("["+Thread.currentThread().getName()+"] 当前静态变量i="+i*2);
}
public static void main(String[] args) {
StaticTest test = new StaticTest();
for(int i=0;i<1000;i++){
new Thread(test,"线程"+i).start();
}
/**
* [线程245] 当前静态变量i=2
* [线程245] 当前静态变量i=4
* [线程246] 当前静态变量i=2
* [线程172] 当前静态变量i=10
* [线程177] 当前静态变量i=5
* [线程177] 当前静态变量i=10
* [线程179] 当前静态变量i=5
* [线程179] 当前静态变量i=10
* [线程171] 当前静态变量i=2
* [线程171] 当前静态变量i=10
* [线程180] 当前静态变量i=5
*/
}
}
测试普通变量 单例
所有的线程一同操作,一个对象中的一个属性,会产生线程争用的情况,所以线程不安全
public class NormalTest implements Runnable {
// 此处定义一个变量
private int i;
/**
* 在线程运行的时候,如果正常的情况下应该输出,2,10
* 如果证明线程不安全,即其他线程修改了别的线程正常运行的值
* 会出现A线程把i修改为5,即将执行i*2=10的时候,其他线程B把i修改为2,使结果输出为4
* A线程把i赋值为2,即将执行输出i=2的时候,线程B把i修改为5,使结果输出为5
* 无论输出4,5都是线程不安全的体现
*/
@Override
public void run() {
i=2;
System.out.println("["+Thread.currentThread().getName()+"] 当前变量i="+i);
i=5;
System.out.println("["+Thread.currentThread().getName()+"] 当前变量i="+i*2);
}
public static void main(String[] args) {
NormalTest test = new NormalTest();
for(int i=0;i<1000;i++){
new Thread(test,"线程"+i).start();
}
/**
* [线程190] 当前变量i=10
* [线程187] 当前变量i=5
* [线程187] 当前变量i=10
* [线程944] 当前变量i=2
* [线程945] 当前变量i=4
* [线程949] 当前变量i=2
*/
}
}
测试普通变量 非单例
每一个对象里面的属性都是线程单独拥有,不会存在互相争用的情况,线程安全
public class NormalTest implements Runnable {
// 此处定义一个变量
private int i;
/**
* 在线程运行的时候,如果正常的情况下应该输出,2,10
*/
@Override
public void run() {
i=2;
System.out.println("["+Thread.currentThread().getName()+"] 当前变量i="+i);
i=5;
System.out.println("["+Thread.currentThread().getName()+"] 当前变量i="+i*2);
}
public static void main(String[] args) {
for(int i=0;i<1000;i++){
new Thread(new NormalTest(),"线程"+i).start();
}
/**
* [线程190] 当前变量i=10
* [线程187] 当前变量i=10
* [线程944] 当前变量i=2
* [线程949] 当前变量i=2
*/
}
}
测试静态方法
静态方法只能引用静态变量和静态方法,引用后等于静态变量的处理
public class NormalTest implements Runnable {
// 此处定义一个静态变量
private static int i;
/**
* 在线程运行的时候,如果正常的情况下应该输出,2,10
* 如果证明线程不安全,即其他线程修改了别的线程正常运行的值
* 会出现A线程把i修改为5,即将执行i*2=10的时候,其他线程B把i修改为2,使结果输出为4
* A线程把i赋值为2,即将执行输出i=2的时候,线程B把i修改为5,使结果输出为5
* 无论输出4,5都是线程不安全的体现
*/
@Override
public void run() {
add();
}
public static void add() {
i=2;
System.out.println("["+Thread.currentThread().getName()+"] 当前变量i="+i);
i=5;
System.out.println("["+Thread.currentThread().getName()+"] 当前变量i="+i*2);
}
public static void main(String[] args) {
NormalTest t = new NormalTest();
for(int i=0;i<1000;i++){
new Thread(t,"线程"+i).start();
}
/**
* [线程161] 当前变量i=2
* [线程161] 当前变量i=4
* [线程251] 当前变量i=2
* [线程794] 当前变量i=10
* [线程818] 当前变量i=5
* [线程818] 当前变量i=10
*/
}
}
测试普通方法
普通方法引用变量后,单例情况下线程不安全,多例下线程安全
没有引用变量,线程安全