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

关于线程安全的问题,一直缺的是总结性的东西,下面分享两篇文章,对于静态变量、实例变量、局部变量的线程的安全问题总结的相当全面,如有遗漏和错误的地方也希望大家指正出来,弥补知识盲区。

转自:http://longw.blog.51cto.com/6475045/1683360

    线程,是我们项目中绕不过的重点领域。提到线程,就常会听到线程安全的术语。那什么是线程安全呢?通俗点说,就是线程访问时不产生资源冲突。其实,这是一个有点难以定义的概念,不是很容易让人一听就懂的概念。“一个类可以被多个线程安全调用就是线程安全的”《java编程并发实践》。

    来说说静态变量、实例变量、局部变量在多线程下的安全问题吧!

    (一)静态变量:线程非安全(如果静态变量没有修改,那么它就是线程安全的;如果有修改的动态,那么它就不是线程安全的)。

   1、静态变量:使用static关键字定义的变量。static可以修饰变量和方法,也有static静态代码块。被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。

    用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它的类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。

    2、静态变量使用的场景:

    (1)对象间共享值时

    (2)方便访问变量时

    3、静态方法使用注意事项:

    (1)不能在静态方法内使用非静态变量,即不能直接访问所属类的实例变量;

    (2)不能在静态方法内直接调用非静态方法;

    (3)静态方法中不能使用this和super关键字;

    4、验证静态变量的线程安全性:

    (1)从程序执行的图中我们可以看出,执行结果中有错误数据,证明了静态变量是存在资源冲突问题的。

    (2)程序运行结果图: 

    

    5、结论:静态变量也称为类变量,属于类对象所有,位于方法区,为所有对象共享,共享一份内存,一旦值被修改,则其他对象均对修改可见,故线程非安全。(不修改不存在线程安全的问题)

    (二)实例变量(成员变量):单例时线程非安全,非单例时线程安全

    1、实例变量:实例变量属于类对象的,也就是说,属于对象实例私有,在虚拟机的堆中分配。

    2、验证实例变量的线程安全性:

    (1)从程序截图中,我们可以看到,当为单例模式时,会产生资源冲突,当非单例模式时,则不会产生线程冲突。每个线程调用的实例对象都是一个新的对象,不存在竞态条件问题。

    (2)程序运行结果图:

    图1:

wKioL1XIYl_RoatmABZU8hrY9Ck087.bmp

    图2:

 3、结论:实例变量是实例对象私有的,系统只存在一个实例对象,则在多线程环境下,如果值改变后,则其它对象均可见,故线程非安全,两个线程访问一个对象会存在竞态条件问题;如果每个线程都在不同的实例对象中执行,则对象与对象间的修改互不影响,故线程安全。

    (三)局部变量:线程安全

     1、局部变量:定义在方法内部的变量。

     2、验证局部变量的安全性:

    (1)从程序截图中可以看出,局部变量在多线程下没有产生资源冲突的问题

    (2)程序运行结果图:

    

    3、结论:每个线程执行时都会把局部变量放在各自的帧栈的内存空间中,线程间不共享,故不存在线程安全问题。

    (四)静态方法的线程安全性

     1、静态方法中如果没有使用静态变量,则没有线程安全的问题;

          静态方法内的变量,每个线程调用时,都会新创建一份,不会公用一个存储单元,故不存在线程冲突的问题。

    以上就是对多线程环境下静态变量、实例变量和局部变量的一点点研究,也仅供自己在需要或遗忘的时候查询参考下了。

线程控制逃逸规则

线程控制逃逸规则可以帮助你判断代码中对某些资源的访问是否是线程安全的。

如果一个资源的创建,使用,销毁都在同一个线程内完成,
且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。

资源可以是对象,数组,文件,数据库连接,套接字等等。Java中你无需主动销毁对象,所以“销毁”指不再有引用指向对象。

即使对象本身线程安全,但如果该对象中包含其他资源(文件,数据库连接),整个应用也许就不再是线程安全的了。比如2个线程都创建了各自的数据库连接,每个连接自身是线程安全的,但它们所连接到的同一个数据库也许不是线程安全的。比如,2个线程执行如下代码:

检查记录X是否存在,如果不存在,插入X

如果两个线程同时执行,而且碰巧检查的是同一个记录,那么两个线程最终可能都插入了记录:

线程1检查记录X是否存在。检查结果:不存在
线程2检查记录X是否存在。检查结果:不存在
线程1插入记录X
线程2插入记录X

同样的问题也会发生在文件或其他共享资源上。因此,区分某个线程控制的对象是资源本身,还是仅仅到某个资源的引用很重要。

总结下:

1、常量:只作为查询,不存在线程安全问题

2、类变量:不修改不存在线程安全问题,修改存在线程安全问题

2、成员变量中:单例存在线程安全问题,多例也就是每次都会新建一个对象不存在线程安全问题

4、局部变量:存在方法体中,不存在线程安全问题。

要想更好的理解成员变量(类变量、实例变量)和局部变量的线程安全问题,需要理解各种变量的初始化的过程已及存放问题,同时理解JVM的内存模型更为重要,任重道远啊。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值