`
yesjavame
  • 浏览: 702407 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

多线程编程 实战篇秘籍 (一)

阅读更多
实战篇秘籍 (一)


本来这几节的内容想拿出来单独放在秘籍篇中.但由于我在实战篇五的结尾处说:
关于这个例子仍然还有很多可说的内容,所以经由这句话所引出的秘籍篇,作为实战篇的
继续,就叫它实战秘籍.

在实战篇五中,有两个非常关键的重点需要在本节介绍的,之所以称之为秘籍,不客气地说
就是很多高手根本没了解的(不是不理解,是压根就不知道)甚至一些在全球小有名气的大牛
都是不很清楚.当然,还是有不少人是非常清楚的,否则我的知识又从哪里来?


在前面的例子中,我们曾经有一个用多个线程打印一个实例字段,以及我在实战篇莫名其妙
地提到doublecheck,如果说打印那个实例字段的目的只是为了说明产生一个实例的多个线程,
那么确切地说,提出doublecheck和在booleanisInterrupted=false;的注释则是一个非常
重要的关注点.

事实上,如果按现有的线程知识,你根本看不出来doublecheck以及目前的
booleanisInterrupted=false;有什么问题,但是如果你在多线程环境下,说不定什么时候
就会发生你意想不到的问题,也许你永远碰不到它的发生,但事实上它确实有问题.

引起问题的关键就是[Java内存模型]

Java是在语言级提供对线程的支持,所以Java的内存模型分为主存储器和工作存储器.

[Mainmemory]主存储器就是实例所在的存储区域,所有实例本身都被放在主存储器中,当然这
句话本身就说明了实例的字段也在主存储器中,主存储器被实例的所有线程所共有.

[workingmemory]工作存储器当然就是每个线程所专有的工作区域,当然其中有它们共有的
主存储器中的一些必要的如实例字段等数据的COPY

当然你千万要知道Java是运行在虚拟系统上的,我们说的主存储器和工作存储器都是在物理内存
中虚拟出来的.是在JVM内部而言的.

在JLS中,对字段的存取被规定为read/write,use/assign,lock/unlock六个最小的操作(action)

对于取而言,当一个线程一次访问实例字段时,首先从主存储器复制该字段的值到工作存储器,
然后线程引用该值.

但是如果同一线程多次访问该字段,是否每次都是从主存储器复制到工作存储器再引用,这由具体
的jvm环境决定.

简单说有可能不从主存储器中复制而直接引用工作区的COPY

同样对于存,如果一个线程一次赋值实例字段,那么会在工作存储器中进行,然后由jvm决定什么时
候映射到主存储器.
而同一个线程如果多次反复对实例字段赋值,那么有可能只对工作存储器的COPY进行,只把最后的
结果同步到主存储器,当然也有可能是每次都把工作存储器和主存储器同步的.这也由具体的JVM决
定.

所以如果多个线程反复对同一实例字段存取,就有可能一个线程对这个字段的改变没有及时反映给
其它线程,因为至少必须被从工作存储器同步到主存储器才能其它线程知道,而在线程专有的工作
存储器中的值其它线程是不可访问的.

所以booleanisInterrupted=false;这一句有可能一个线程中断后在这个线程工作的范围内已经
设为true,但还没有立即被映射到主存储器时,其它线程还不知道.

同样,对于doublecheck,我们来看它的问题:
在java与模式一书中,作者这对个问题的说明根本没有正确地理解.而且他一再说明是错误的例子
其实是正确的.

publicMyObject{
privatestaticMyObectobj;
publicstaticgetInstance(){
if(obj==null){
synchronized(MyObj.class){
if(obj==null)
obj=newMyObject();
}
}
returnobj;
}
}

这个例子根本不会存在问题.因为线程的同步保证了不会在同步块外部发生多线程调,而在同步块中
只有一个线程能执行,其赋值的结果在离开同步块时会强制映射到主工作区,也就是对obj的赋值一定
会在主工作区反映出来.所以作者举这个例子说明他根本没有理解为什么doublecheck是不安全的.

我们来看下面的例子:
publicMyObject{
privatestaticMyObectobj;
privateDated=newData();
publicDatagetD(){returnthis.d;}
publicstaticMyObect getInstance(){
if(obj==null){
synchronized(MyObect.class){
if(obj==null)
obj=newMyObject();//这里
}
}
returnobj;
}
}
一个线程A运行到"这里"时,对于A的工作区中,肯定已经产生一个MyObect对象,而且这时这个对象已经
完成了Datad.现在线程A调用时间到,执行权被切换到另一个线程B来执行,会有什么问题呢?

如果obj不为null,线程B获得了一个obj,但可能obj.getD()却还没有初始化.

为什么?既然obj已经可见了(线程A还没有离开同步块),而d却不可见呢?
如果d不可见,那么obj也应该为null啊?线程B应该等待A释放同步块啊?

事实上,对于"这里"这条语句,线程A还没有离开同步块.
因为没有"离开同步块"这个条件,线程a的工作区没有强制与主存储器同步,这时工作区中有两个字段
obj,d到底先把谁同步到主存储区,没有条件限制,虽然在线程A的工作区obj和d都是完整的,但有JSL
没有强制不允许先把obj映射到主存储区,如果哪个jvm实现按它的优化方案先把工作存储器中的obj
同步到主存储器了,这时正好线程B获取了,而d却没有同步过去,那么线程B就获取了obj的引用却找不能
obj.getD();

这个问题是否真的会发生?

我个人的意见,从理论上是会发生的,所以如果是设计银行交易系统你就没有必要为提高一些性能而放
弃安全性,而如果只是一般的应用,发生这种情况的机率本来就非常发生也不会有太多的危害,我认为
可以不去介意.比如获取数据库连结,即使获取不到也不过让访问者再刷新一次重新查询而已.事实上有
可能几天,几个月,一年都不会发生一次.知道小行星有可能会撞地球的,但你不要太在意而影响你正常
的生活.如果你不是做银行交易系统的你不要太担心,而大多数JVM实现本身就会保证这种安全的.也就
在把obj同步到主存储器会先同步d,但万一发生相反的顺序,你就无权责备你所用的JVM.只是这种万一
机会不是很多.


当然如果是贪婪式调用,静态实例的初始化由ClassLoader经由[ThreadSafe]来完成,当然就不会有这
个问题了:
publicMyObject{
privatestaticMyObectobj=newMyObject();
privateDated=newData();
publicDatagetD(){returnthis.d;}
publicstaticgetInstance(){
returnobj;
}
}


那么如何保证实例字段能在工作存储区能被即时映,下一节我们来讲最不常用的关键字:

[volatile]

分享到:
评论

相关推荐

    Java多线程编程实战指南(核心篇)

    Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...

    Java多线程编程实战指南-核心篇

    《Java多线程编程实战指南-核心篇》是一本深入探讨Java并发编程的书籍,旨在帮助读者掌握在Java环境中创建、管理和同步线程的核心技术。Java的多线程能力是其强大之处,使得开发者能够在同一时间执行多个任务,提高...

    C#多线程编程实战.pdf

    C#多线程编程实战.pdf

    汪文君JAVA多线程编程实战(完整不加密)

    《汪文君JAVA多线程编程实战》是一本专注于Java多线程编程的实战教程,由知名讲师汪文君倾力打造。这本书旨在帮助Java开发者深入理解和熟练掌握多线程编程技术,提升软件开发的效率和质量。在Java平台中,多线程是...

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

    C#多线程编程实例实战.pdf

    C#多线程编程实例实战 C#多线程编程实例实战是指在C#语言中实现多线程编程的实例和实战经验。多线程编程是指在同一个进程中创建多个线程,以便提高程序的执行效率和响应速度。在C#语言中,可以使用Thread类和线程池...

    java多线程编程实战指南 核心篇 代码

    《Java多线程编程实战指南(核心篇)》以基本概念、原理与方法为主线,辅以丰富的实战案例和生活化实例,并从Java虚拟机、操作系统和硬件多个层次与角度出发,循序渐进、系统地介绍Java平台下的多线程编程核心技术及...

    c#多线程编程实战(原书第二版)源码

    《C#多线程编程实战(原书第二版)源码》是一本深入探讨C#中多线程技术的专业书籍,其源码提供了丰富的实践示例,帮助读者掌握并发编程的核心概念和技术。在C#中,多线程是实现高性能、响应式应用程序的关键组成部分...

    C#多线程编程实战完整源码

    本资源"《C#多线程编程实战》完整源码"提供了丰富的实例,适用于学习和实践C#中的多线程概念。 在C#中,多线程允许应用程序同时执行多个独立的任务,提高系统利用率并优化性能。.NET框架为开发者提供了强大的支持,...

    C++多线程编程实战

    C++多线程 windows

    C ++多线程编程实战姜佑译.rar

    《C++多线程编程实战》是一本深入探讨C++并发编程领域的专著,由姜佑译成中文。这本书旨在帮助读者理解并掌握C++中的多线程技术,从而能够利用这种技术创建高效的并行应用程序。C++作为一门强大且灵活的编程语言,其...

    《C#多线程编程实战》.((美)Eugene Agafonov).[PDF]

    《C#多线程编程实战》.((美)Eugene Agafonov).[PDF]《C#多线程编程实战》.((美)Eugene Agafonov).[PDF]《C#多线程编程实战》.((美)Eugene Agafonov).[PDF]

    多线程编程实战指南-核心篇

    《多线程编程实战指南-核心篇》是针对Java开发者深入理解并掌握多线程编程的一本实战性书籍。在当今的并发计算环境中,多线程技术是必不可少的知识点,它能够有效地利用多核处理器资源,提高程序的执行效率。本书以...

    【免费2018】C#多线程编程实战_中文完整版(带书签目录)【PDF高清】.rar

    《C#多线程编程实战》是一本专为C#开发者设计的深度学习多线程技术的书籍。这本书全面覆盖了C#中的并发和多线程编程,旨在帮助读者掌握如何在多核处理器环境下有效利用系统资源,提高程序的执行效率和响应速度。 在...

    多线程编程——实战篇

    ### 多线程编程实战篇知识点详解 #### 一、多线程编程的基本原则 多线程编程在软件开发中占据着重要的地位,特别是在需要高效利用计算机资源的应用场景下。多线程编程的核心在于如何有效地管理和协调多个线程之间...

    C++多线程编程实战 _C++_线程进程_thread_

    P303.zip

    C#多线程编程实战 源代码

    本书是一本通俗易懂的C#多线程编程指南,通过70多个容易理解的示例,循序渐进地讲解C#5.0中的异步及并发编程,引导读者了解Windows下C#多线程编程的多样性。 通过阅读本书,你将学到: 使用原始线程、异步线程,...

    C++ 多线程编程实战

    高清文档,希望对C++学习者有用

    Java多线程编程实战指南(设计模式篇)

    《Java多线程编程实战指南(设计模式篇)》由黄文海撰写,是一本深入探讨Java多线程编程和设计模式的专业书籍。书中详细介绍了如何在Java环境中利用多线程来实现高效的并发处理,同时结合设计模式,帮助开发者更好地...

    C#多线程编程实战源码

    本资源"《C#多线程编程实战源码》"提供了丰富的实例,帮助开发者深入理解和实践C#中的多线程技术。 1. **线程基础**:线程是操作系统分配CPU时间的基本单元,一个进程可以有多个线程,每个线程执行不同的任务。在C#...

Global site tag (gtag.js) - Google Analytics