您的位置:首页 > 编程语言 > Java开发

内存一致性错误和线程干扰

2012-06-01 15:59 916 查看
内存一致性错误
          造成内存一致性错误的原因很多也很复杂,不同的CPU架构,不同的指令读取模式,不同的内存管理模式等都会造成内存不一致错误。通常服务器提供商会提供一些策略以便获得更好的计算性能,但是获得更好性能的同时也会造成内存一致性错误,所以服务器提供商又会提供一些新的策略来规避内存一致性错误。就跟我们平时编程时候一样,没有一种策略可以解决所有的问题,要根据业务进行取舍。服务器提供商遇到的问题和我们大致一样,所以提供商也提供多种策略供开发者选择,有的策略计算能力较强,但是更容易产生内存一致性错误。有的则刚好相反。服务器提供商很多,提供的策略也不相同,开发者也需要了解程序运行机器上都提供了那些策略,通常服务器提供商会与开发者做一个约定,什么样的statement会采用什么样的策略,这个约定也叫做内存模型(memory
model)。对于开发者来说,这是一个很大的工作量,写完的程序在一台机器上会运行的很好,而在另一台机器上会产生很奇怪的结果。因为两台机器不能担保程序的statement会按照相同的方式来执行。好在我们不是直接与硬件直接交互,我们在操作系统上编程,操作系统帮我们同化了不同的硬件。如果程序是跑在虚拟机(jvm或者.net
framework)上,那么虚拟机也会对指令进行优化,虚拟机也会对开发者提供内存模型,从而达到性能与内存一致性的平衡。通过上述的描述,我们似乎没有办法为内存一致性错误的运行状态进行一个清晰的描述。我们能做的只是按照内存模型中的约定(内存模型中的约定也可以称为happens-before
relationship)去编程。

线程干扰
多个线程操作一个变量就有可能产生线程干扰。线程干扰指的是:一个变量a,由thread1和thread2同时计算,thread2忽略了thread1的运算,或者反之。举个例子:有一个statement  c++,由thread1,thread2执行。c++这个statement产生的指令是:

第一步:获得c的当前值

第二步:对第一步返回的值+1

第三部:将第二步运算结果存储到c中。

假定当前c的值为0,如果thread1执行完第三步后,thread2执行第一步,那么将返回2。如果thread1执行步骤一或者步骤二时,thread2执行步骤一。那么结果将返回1。

乍一看,与内存一致性错误有些相像,但是他们造成错误的原因不同。如果是内存一致性错误,有可能即使thread1执行完第三步后,thread2再开始执行第一步,结果也有可能是1。因为thread2的第一步看不到thread1的第三步的执行结果。

总结

在编写多线程程序时,不但要注意线程干扰,也要考虑内存一致性错误。

另外在java中提供了三个并发包java.util.concurrent,java.util.concurrent.atomic,java.util.concurrent.locks。他们的主要用途并非是对现有的线程语义(Synchronization,wait,notify,interrupt,join)进行升级,而是利用现有语义开发一系列业务模型,开发者在编写多线程程序时应先思考自己的业务模型是否与java提供的模型吻合,如果吻合应优先使用java.util.concurrent,java.util.concurrent.atomic,java.util.concurrent.locks包下的类。这些类都是oracle公司的并发专家所编写的,可以很好的避免线程干扰和内存一致性错误,也可以避免死锁,饿死锁,活跃锁等常见的问题,同时避免由于大量的Synchronization而造成的效率低下问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息