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

来源:互联网 发布:掌趣上游网络 编辑:程序博客网 时间:2024/05/21 09:24

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



(原文地址:http://blog.csdn.net/will_awoke/article/details/38044415)



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

   写道

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

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

 

静态成员变量线程非安全(无论单例或者非单例皆不安全)。

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


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

实例变量为对象实例私有,在虚拟机的堆heap中分配,若在系统中只存在一个此对象的实例,在多线程环境下,“犹如”静态变量那样,被某个线程修改后,其他线程对修改均可见,故线程非安全(如,springmvc controller是单例的,非线程安全的);如果每个线程执行都是在不同的对象中,那对象与对象之间的实例变量的修改将互不影响,故线程安全(如,struts2 action默认是非单例的,每次请求在heap中new新的action实例,故struts2 action可以用实例成员变量)。


局部变量线程安全线程封闭性Thread Confinement

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


〖by self:上述所有的变量均指,共享的(Shared)和可变的(Mutable,进行了'read''write'操作)状态变量,只有对这样的变量讨论线程安全才有意义,所有实际上上述的“局部变量”一定是线程安全的,因为其不是共享的(Not Shared),至于非状态变量,毫无疑问,是线程安全的。〗



一、静态成员变量 


线程安全问题模拟:

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

[java] view plain copy
  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*2的值:" + 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] view plain copy
  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*2的值:" + 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] view plain copy
  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. }    

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

 


四、静态方法 是否是线程安全的:


先看一个类

[java] view plain copy
  1. public class  Test{  
  2. public static  String hello(String str){  
  3.     String tmp="";  
  4.     tmp  =  tmp+str;  
  5.    return tmp;  
  6. }  
  7. }  

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

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

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

 

注意,静态成员变量,由于是在类加载时占用一个存储区,每个线程都是共用这个存储区的,所以如果在静态方法里使用了静态成员变量,这就会有线程安全问题。即只要方法内含有静态成员变量,就是非线程安全的,(实际上归根到底还是变量的线程安全问题~)。



阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 桔子树根部霉了怎么办 电脑u盘被禁用怎么办 机房u盘限制了怎么办 蜘蛛爬到人身上怎么办 房间里有蛐蛐叫怎么办 养的雨林蝎跑了怎么办 多肉长得太高了怎么办 手被蝎子咬了怎么办 被蝎子蛰住了怎么办 小孩被蝎子蜇了怎么办 孕妇被蝎子蛰了怎么办 怀孕被蝎子蜇了怎么办 孩子被蝎子蛰了怎么办 手被蝎子蛰了怎么办 被东亚钳蝎蛰了怎么办 孕37周羊水过多怎么办 孕38周羊水过多怎么办 孕39周羊水过多怎么办 生完孩子腿关节疼怎么办 手指上长了倒刺怎么办 肥肉吃多了恶心怎么办 大便粘稠怎么回事还便秘怎么办 狗狗大便次数多怎么办 宝宝拉白色稀便怎么办 5个月宝宝流鼻涕怎么办 5个月婴儿流鼻涕怎么办 五个月宝宝流鼻涕鼻塞怎么办 4个月宝宝流鼻涕怎么办 大便经常是稀的怎么办 拉黑色的稀大便怎么办 孕晚期半夜饿了怎么办 肠胃不好大便不成型怎么办 螃蟹吃多了过敏怎么办 吃螃蟹过敏很痒怎么办 吃螃蟹喝啤酒了怎么办 1个月宝宝拉肚子怎么办 被雨林蝎子蛰了怎么办 在家被蜈蚣咬了怎么办 宝宝被蝎子蜇了怎么办 仓鼠只吃面包虫怎么办 套装但是装等低怎么办