Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 911|回复: 0

JVM原理学习笔记

[复制链接]

该用户从未签到

发表于 2011-7-25 16:47:30 | 显示全部楼层 |阅读模式
关于“单例同步”:
    一直有人在问单例对象的并发调用是否需要同步,基本属于“月经帖”了,答案是现成的满天下都是,但真正能让人心里踏实下来的解释寥寥无几。实际上,只要学习了一些JVM的运行原理,解释这个问题就不难了。
    如果一个类是单例的,比如某些DAO的设计,那么所有的线程来访问这个类的实例的时候,它们获得的都将是同一个对象,这是不言自明的。如果这些线程的当前操作是“互斥”的,那么每个线程就必须在取得该实例的访问资格的时候为该对象上锁,以独享该对象直到当前操作结束,以免在操作中途被其它线程介入而产生不可预知的结果。问题是,什么样的操作是“互斥”的呢?
    简单地说,互斥操作就是两个操作企图对它俩共享的某个资源进行修改,而修改的结果是不可预知的。于是问题就变成了,什么才是“共享的资源”?从纯粹 java语法的角度这个问题没法解释,因为它遵循的是当前java虚拟机的规范描述。现假设两个线程正企图同时访问一个单例对象的方法,如,
view plaincopy to clipboardprint?
int method1(int i) {     
    int j = 3;      
    return i+j;     
}   
int method1(int i) {  
     int j = 3;   
     return i+j;  
}
一个规范的虚拟机线程在调用method1()的时候是这样做的:
    1) 把method1()的局部变量,包括参数,压入当前线程的栈;
    2) 从当前线程栈弹出变量j,并赋予数值3;
    3) 从当前线程栈弹出参数i,与j执行加法运算;
    4) 从当前线程栈中释放当前方法占用的栈帧,并把method1()的结果压入当前线程栈。
需要说明的是,当前线程栈是当前线程独有的,绝对不会被其它线程访问到。这样,只要你在method1()里面使用的全都是局部变量或参数,那就不需要为多线程的并发调用发愁,因为每个线程都有自己的栈帧,各不相干。
    复杂一点,如果method1()是这样定义的:
view plaincopy to clipboardprint?
int method1(int i, SingletonClass singleObj) {     
     singleObj.intValue ++;     
     int j = i + singleObj.intValue;     
     return j;     
}   
   int method1(int i, SingletonClass singleObj) {  
        singleObj.intValue ++;  
        int j = i + singleObj.intValue;  
        return j;  
    }  
这下我们就不得不考虑线程同步问题了,这个方法显然包含了一个互斥的操作“singleObj.intValue ++;”。前面说过,方法的参数会被压入当前线程私有的栈直到方法结束,但这里要注意的是,singleObj只是一个引用地址而非真正的对象实例,因此,尽管 singleObj这个引用值是被压入线程私有栈去的,但真正的对象实例却是在堆里存放的,栈虽然是线程私有的,堆却是所有线程共享的,因此 singleObj的成员变量intValue是完全有可能在当前线程执行第二行代码前被其它线程修改了的。比如说,线程1调用mothod1()的时候 singleObj.intValue的值是1, i的值是2,那么正确的情况下,method1()的返回值应该是4。但当线程1和线程2几乎同时调用method1(),线程2恰好在线程1把 intValue变成2之后的一瞬间又执行了一次singleObj.intValue ++,由于singleObj是单例,两个线程遇到的singleObj是同一个对象,因此这次运算将把intValue变成3。接下来线程1继续第二行代码,结果j的结果变成了i+3 = 2+3 = 5 。 如此一来,线程1调用method1()的返回结果究竟会是 4 还是 5 是无法确定的,只能凭运气,寄望线程2在线程1从调用method1()到取得返回值之间的这段时间打盹。在绝大多数情况下,这种“凭运气”的做法是不能接受的,我们需要向线程1保证,在它调用method1()期间绝不会收到线程2的干扰。做法如下:
view plaincopy to clipboardprint?
int method1(int i, SingletonClass singleObj) {     
    int j = 0;     
    synchronize(singleObj) {     
        singleObj.intValue ++;     
        j = i + singleObj.intValue;     
    }     
    return j;     
}   
    int method1(int i, SingletonClass singleObj) {  
        int j = 0;  
        synchronize(singleObj) {  
            singleObj.intValue ++;  
            j = i + singleObj.intValue;  
        }  
        return j;  
    }  
这个写法仍然有缺陷,因为线程2很可能在线程1执行int j = 0 的时候修改singleObj的intValue,所以比较可靠的应该在调用method1()之前锁住singleObj:
view plaincopy to clipboardprint?
synchronize(singleObj) {     
    int result = obj.method1(2, singleObj);     
}   
    synchronize(singleObj) {  
        int result = obj.method1(2, singleObj);  
    }  
小小总结一下,“一个方法如果涉及对某个共享对象(或堆对象)的写操作,那么它必须同步该对象”这个说法在大多数情况下都对,但还有些失之笼统,或许这样说比较准确些,“如果一个方法对某共享对象的写操作会造成其它线程返回值的不确定性,则该方法应该同步该对象。”
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

GMT+8, 2024-5-13 09:02 , Processed in 0.469579 second(s), 48 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表