`
shaomeng95
  • 浏览: 218962 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

java线程安全问题之静态变量、实例变量、局部变量

阅读更多

       java多线程编程中,存在很多线程安全问题,至于什么是线程安全呢,给出一个通俗易懂的概念还是蛮难的,如同《java并发编程实践》中所说:

写道
给线程安全下定义比较困难。存在很多种定义,如:“一个类在可以被多个线程安全调用时就是线程安全的”。

 此处不赘述了,首先给出静态变量、实例变量、局部变量在多线程环境下的线程安全问题结论,然后用示例验证,请大家擦亮眼睛,有错必究,否则误人子弟!

 

静态变量:线程非安全。

静态变量即类变量,位于方法区,为所有对象共享,共享一份内存,一旦静态变量被修改,其他对象均对修改可见,故线程非安全。

实例变量:单例模式(只有一个对象实例存在)线程非安全,非单例线程安全。

实例变量为对象实例私有,在虚拟机的堆中分配,若在系统中只存在一个此对象的实例,在多线程环境下,“犹如”静态变量那样,被某个线程修改后,其他线程对修改均可见,故线程非安全;如果每个线程执行都是在不同的对象中,那对象与对象之间的实例变量的修改将互不影响,故线程安全。

局部变量:线程安全。

每个线程执行时将会把局部变量放在各自栈帧的工作内存中,线程间不共享,故不存在线程安全问题。

 

静态变量线程安全问题模拟:

----------------------------------------------------------------------------------

/** 
  * 线程安全问题模拟执行 
  *  ------------------------------ 
  *       线程1      |    线程2 
  *  ------------------------------ 
  *   static_i = 4;  | 等待 
  *   static_i = 10; | 等待 
  *    等待          | static_i = 4; 
  *   static_i * 2;  | 等待 
  *  -----------------------------
 * */
public class Test implements Runnable
{
    private static int static_i;//静态变量 
    
    public void run()
    {
        static_i = 4;
        System.out.println("[" + Thread.currentThread().getName()
                + "]获取static_i 的值:" + static_i);
        static_i = 10;
        System.out.println("[" + Thread.currentThread().getName()
                + "]获取static_i*3的值:" + static_i * 2);
    }
    
    public static void main(String[] args)
    {
        Test t = new Test();
        //启动尽量多的线程才能很容易的模拟问题 
        for (int i = 0; i < 3000; i++)
        {
            //t可以换成new Test(),保证每个线程都在不同的对象中执行,结果一样 
            new Thread(t, "线程" + i).start();
        }
    }
}

 

 

根据代码注释中模拟的情况,当线程1执行了static_i = 4;  static_i = 10; 后,线程2获得执行权,static_i = 4; 然后当线程1获得执行权执行static_i * 2;  必然输出结果4*2=8,按照这个模拟,我们可能会在控制台看到输出为8的结果。

写道
[线程27]获取static_i 的值:4
[线程22]获取static_i*2的值:20
[线程28]获取static_i 的值:4
[线程23]获取static_i*2的值:8
[线程29]获取static_i 的值:4
[线程30]获取static_i 的值:4
[线程31]获取static_i 的值:4
[线程24]获取static_i*2的值:20

 看红色标注的部分,确实出现了我们的预想,同样也证明了我们的结论。

 

实例变量线程安全问题模拟:

----------------------------------------------------------------------------------

public class Test implements Runnable
{
    private int instance_i;//实例变量
    
    public void run()
    {
        instance_i = 4;
        System.out.println("[" + Thread.currentThread().getName()
                + "]获取instance_i 的值:" + instance_i);
        instance_i = 10;
        System.out.println("[" + Thread.currentThread().getName()
                + "]获取instance_i*3的值:" + instance_i * 2);
    }
    
    public static void main(String[] args)
    {
        Test t = new Test();
        //启动尽量多的线程才能很容易的模拟问题 
        for (int i = 0; i < 3000; i++)
        {
            //每个线程对在对象t中运行,模拟单例情况
            new Thread(t, "线程" + i).start();
        }
    }
}

 

 

按照本文开头的分析,犹如静态变量那样,每个线程都在修改同一个对象的实例变量,肯定会出现线程安全问题。

写道

[线程66]获取instance_i 的值:10
[线程33]获取instance_i*2的值:20
[线程67]获取instance_i 的值:4
[线程34]获取instance_i*2的值:8
[线程35]获取instance_i*2的值:20
[线程68]获取instance_i 的值:4

 

看红色字体,可知单例情况下,实例变量线程非安全。

 

将new Thread(t, "线程" + i).start();改成new Thread(new Test(), "线程" + i).start();模拟非单例情况,会发现不存在线程安全问题。

 

 

局部变量线程安全问题模拟:

----------------------------------------------------------------------------------

 

public class Test implements Runnable
{
    public void run()
    {
        int local_i = 4;
        System.out.println("[" + Thread.currentThread().getName()
                + "]获取local_i 的值:" + local_i);
        local_i = 10;
        System.out.println("[" + Thread.currentThread().getName()
                + "]获取local_i*2的值:" + local_i * 2);
    }
    
    public static void main(String[] args)
    {
        Test t = new Test();
        //启动尽量多的线程才能很容易的模拟问题
        for (int i = 0; i < 3000; i++)
        {
            //每个线程对在对象t中运行,模拟单例情况 
            new Thread(t, "线程" + i).start();
        }
    }
}

 

 

控制台没有出现异常数据。

 

---------------------------------------------------------------

以上只是通过简单的实例来展示静态变量、实例变量、局部变量等的线程安全问题,

并未进行底层的分析,下一篇将对线程问题的底层进行剖析。

 

 

分享到:
评论
2 楼 LinApex 2013-11-30  
shaomeng95 写道
iteye的编辑器太差了,唉

这个分析测试的很好,学习了
1 楼 shaomeng95 2011-04-18  
iteye的编辑器太差了,唉

相关推荐

    g++中的局部静态变量的初始化机制及线程安全

    g++中的局部静态变量的初始化机制及线程安全

    《深入理解JAVA内存模型》PDF

    局部变量(Local variables),方法定义参数(java语言规范称之为formal method parameters)和异常处理器参数(exception handler parameters)不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的...

    Java开发技术大全(500个源代码).

    localVSmember.java 局部变量与成员变量同名问题示例 onlyTest.java 对象传值示例 otherClass.java 从类的外部访问对象的成员 showInstVar.java 演示不同的对象拥有不同的成员变量 showMain.java 演示main方法...

    JAVA 范例大全 光盘 资源

    实例4 变量和常量 9 实例5 基本数据类型转换 10 实例6 操作多种运算符 12 实例7 不同数制间的转换 17 实例8 多种方式实现阶乘的算法 20 第3章 流程控制语句 23 实例9 打印任一年日历 23 实例10 控制台输出...

    Linux中的线程局部存储(1)

    在Linux系统中使用C/C++进行多线程编程时,我们遇到多的是对同一...  在C/C++程序中常存在全局变量、函数内定义的静态变量以及局部变量,对于局部变量来说,其不存在线程安全问题,因此不在本文讨论的范围之内。全局变

    java编程基础,应用与实例

    5.4 局部变量和成员变量 67 5.5 this引用 68 5.6 静态变量与静态方法 70 5.7 成员与静态方法的关系 71 5.8 包与导入 72 5.9 访问控制符 74 5.10 重载 77 5.11 构造函数 79 5.12 类的初始化 83 ...

    java范例开发大全

    实例154 使用静态成员变量计算内存中实例化的对象数目 239 实例155 实现加减乘除的方法 240 8.3 面向对象的设计模式 241 实例156 Singleton单例模式 242 实例157 招聘(简单工厂模式) 243 实例158 同学聚会(工厂...

    线程安全的单例模式及其实现

    懒汉模式,不加锁 懒汉模式,加锁 懒汉模式,不加锁但是静态局部变量实现了线程安全

    Java范例开发大全(全书源程序)

    实例154 使用静态成员变量计算内存中实例化的对象数目 239 实例155 实现加减乘除的方法 240 8.3 面向对象的设计模式 241 实例156 Singleton单例模式 242 实例157 招聘(简单工厂模式) 243 实例158 同学聚会...

    java范例开发大全源代码

     实例154 使用静态成员变量计算内存中实例化的对象数目 239  实例155 实现加减乘除的方法 240  8.3 面向对象的设计模式 241  实例156 Singleton单例模式 242  实例157 招聘(简单工厂模式) 243  ...

    Java范例开发大全 (源程序)

     实例154 使用静态成员变量计算内存中实例化的对象数目 239  实例155 实现加减乘除的方法 240  8.3 面向对象的设计模式 241  实例156 Singleton单例模式 242  实例157 招聘(简单工厂模式) 243  实例158...

    java范例开发大全(pdf&源码)

    实例154 使用静态成员变量计算内存中实例化的对象数目 239 实例155 实现加减乘除的方法 240 8.3 面向对象的设计模式 241 实例156 Singleton单例模式 242 实例157 招聘(简单工厂模式) 243 实例158 同学聚会(工厂...

    Java常见面试问题整理.docx

    2.Java虚拟机栈:描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧 ,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    实例137 使用线程局部变量实现线程同步 177 实例138 简单的线程通信 179 实例139 简单的线程死锁 180 实例140 解决线程的死锁问题 182 6.3 线程的进阶 183 实例141 使用阻塞队列实现线程同步 183 实例142 新建有...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    7.8.1 发现问题:当实例变量和局部变量重名 177 7.8.2 经常深藏不露的this关键字 178 7.8.3 在方法中调用方法 179 7.9 构造方法(Constructor) 181 7.9.1 构造(Constructor)方法初探 181 7.9.2 如何使用构造...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    7.8.1 发现问题:当实例变量和局部变量重名 177 7.8.2 经常深藏不露的this关键字 178 7.8.3 在方法中调用方法 179 7.9 构造方法(Constructor) 181 7.9.1 构造(Constructor)方法初探 181 7.9.2 如何使用构造...

    Android面试(一)Java虚拟机内存结构分析

    虚拟机栈:线程创建之初,Java虚拟机会为每一个线程开辟一块虚拟机栈空间,存储线程方法调用的局部变量,计算中间量,参数等,是线程私有的内存区域。 本地方法栈:线程私有的用于native方法引用的内存栈空间。 程序...

    Java优化编程(第2版)

    1.11.2 引用类中的静态变量与方法的 …… 小结 第4章 java核心类与性能优化 4.1 散列表类与性能优化 4.1.1 线程同步散列表类 4.1.2 设置arraylist初始化容量 4.1.3 arraylist与linkedlist 4.2 string类与性能优化 ...

Global site tag (gtag.js) - Google Analytics