欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

多线程中 静态变量,普通变量(实例变量),局部变量,静态方法,普通方法 的线程安全问题

程序员文章站 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
         */
    }
}

测试普通方法

普通方法引用变量后,单例情况下线程不安全,多例下线程安全
没有引用变量,线程安全

相关标签: java thread