zoukankan      html  css  js  c++  java
  • java线程安全问题之静态变量、实例变量、局部变量

     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,每个线程都会创建自己的一份,因此不会有线程安全问题

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

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

  • 相关阅读:
    HDU 5486 Difference of Clustering 图论
    HDU 5481 Desiderium 动态规划
    hdu 5480 Conturbatio 线段树 单点更新,区间查询最小值
    HDU 5478 Can you find it 随机化 数学
    HDU 5477 A Sweet Journey 水题
    HDU 5476 Explore Track of Point 数学平几
    HDU 5475 An easy problem 线段树
    ZOJ 3829 Known Notation 贪心
    ZOJ 3827 Information Entropy 水题
    zoj 3823 Excavator Contest 构造
  • 原文地址:https://www.cnblogs.com/deepalley/p/12350333.html
Copyright © 2011-2022 走看看