`

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

    博客分类:
  • Java
 
阅读更多

静态变量:线程非安全。

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

 

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

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

局部变量:线程安全。

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

 

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

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

 

Java代码  收藏代码
  1. /**  
  2.   * 线程安全问题模拟执行  
  3.   *  ------------------------------  
  4.   *       线程1      |    线程2  
  5.   *  ------------------------------  
  6.   *   static_i = 4;  | 等待  
  7.   *   static_i = 10; | 等待  
  8.   *    等待          | static_i = 4;  
  9.   *   static_i * 2;  | 等待  
  10.   *  ----------------------------- 
  11.  * */  
  12. public class Test implements Runnable  
  13. {  
  14.     private static int static_i;//静态变量   
  15.       
  16.     public void run()  
  17.     {  
  18.         static_i = 4;  
  19.         System.out.println("[" + Thread.currentThread().getName()  
  20.                 + "]获取static_i 的值:" + static_i);  
  21.         static_i = 10;  
  22.         System.out.println("[" + Thread.currentThread().getName()  
  23.                 + "]获取static_i*3的值:" + static_i * 2);  
  24.     }  
  25.       
  26.     public static void main(String[] args)  
  27.     {  
  28.         Test t = new Test();  
  29.         //启动尽量多的线程才能很容易的模拟问题   
  30.         for (int i = 0; i < 3000; i++)  
  31.         {  
  32.             //t可以换成new Test(),保证每个线程都在不同的对象中执行,结果一样   
  33.             new Thread(t, "线程" + i).start();  
  34.         }  
  35.     }  
  36. }  

 

 

 

根据代码注释中模拟的情况,当线程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

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

 

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

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

Java代码  收藏代码
  1. public class Test implements Runnable  
  2. {  
  3.     private int instance_i;//实例变量  
  4.       
  5.     public void run()  
  6.     {  
  7.         instance_i = 4;  
  8.         System.out.println("[" + Thread.currentThread().getName()  
  9.                 + "]获取instance_i 的值:" + instance_i);  
  10.         instance_i = 10;  
  11.         System.out.println("[" + Thread.currentThread().getName()  
  12.                 + "]获取instance_i*3的值:" + instance_i * 2);  
  13.     }  
  14.       
  15.     public static void main(String[] args)  
  16.     {  
  17.         Test t = new Test();  
  18.         //启动尽量多的线程才能很容易的模拟问题   
  19.         for (int i = 0; i < 3000; i++)  
  20.         {  
  21.             //每个线程对在对象t中运行,模拟单例情况  
  22.             new Thread(t, "线程" + i).start();  
  23.         }  
  24.     }  
  25. }  

 

 

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

写道

[线程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();模拟非单例情况,会发现不存在线程安全问题。

 

 

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

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

 

Java代码  收藏代码
  1. public class Test implements Runnable  
  2. {  
  3.     public void run()  
  4.     {  
  5.         int local_i = 4;  
  6.         System.out.println("[" + Thread.currentThread().getName()  
  7.                 + "]获取local_i 的值:" + local_i);  
  8.         local_i = 10;  
  9.         System.out.println("[" + Thread.currentThread().getName()  
  10.                 + "]获取local_i*2的值:" + local_i * 2);  
  11.     }  
  12.       
  13.     public static void main(String[] args)  
  14.     {  
  15.         Test t = new Test();  
  16.         //启动尽量多的线程才能很容易的模拟问题  
  17.         for (int i = 0; i < 3000; i++)  
  18.         {  
  19.             //每个线程对在对象t中运行,模拟单例情况   
  20.             new Thread(t, "线程" + i).start();  
  21.         }  
  22.     }  
  23. }  

 

 

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

 

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

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

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

 

 

静态方法是线程安全的

 

 

先看一个类

public class  Test{

public static  String hello(String str){

    String tmp="";

    tmp  =  tmp+str;

   return tmp;

}

}

hello方法会不会有多线程安全问题呢?没有!!

静态方法如果没有使用静态变量,则没有线程安全问题。

为什么呢?因为静态方法内声明的变量,每个线程调用时,都会新创建一份,而不会共用一个存储单元。比如这里的tmp,每个线程都会创建自己的一份,因此不会有线程安全问题

 

注意,静态变量,由于是在类加载时占用一个存储区,每个线程都是共用这个存储区的,所以如果在静态方法里使用了静态变量,这就会有线程安全问题!

总结:只要方法内含有静态变量,就是非线程安全的

 

 

分享到:
评论

相关推荐

    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