JAVA高级多核线程volatile原理与技巧

文章描述:-2022年4月14日发(作者:全思诚)JAVA高级:多核线程-volatile原理与技巧为什么使用volatile比同步代价更低?同步的代价,主要由其覆盖范围决定,如果可以降低同步的覆盖范围,则可以大幅提升程序性能.而volatile的覆盖范围仅仅变量级别的.因此它的同步代价很低.volatile原理是什么?volatile的语义,其实是告诉处理器,不要将我放入工作内存,请直接在主存操作我.(工

-

JAVA高级多核线程volatile原理与技巧
2022年4月14日发
(作者:全思诚)

JAVA高级:多核线程-volatile原理与技巧为什么使用volatile比同步代价更低?同步的代价,主要由其覆盖范围决定,如果可以降低同步的覆盖范围,则可以大幅提升

程序性能.而volatile的覆盖范围仅仅变量级别的.因此它的同步代价很低.volatile原理是什么?volatile的语义,其实是告诉处理器,不要将我放入工作内存,请直接在主存操作我.(工

作内存详见java内存模型)因此,当多核或多线程在访问该变量时,都将直接操作主存,这从本质上,做到了变量

共享.volatile的有什么优势?1,更大的程序吞吐量2,更少的代码实现多线程3,程序的伸缩性较好4,比较好理解,无需太高的学习成本volatile有什么劣势?1,容易出问题2,比较难设计volatile运算存在脏数据问题volatile仅仅能保证变量可见性,无法保证原子性.volatile的racecondition示例:

publicclassTestRaceCondition{

privatevolatileinti=0;

publicvoidincrease(){

i++;

}

publicintgetValue(){

returni;

}

}当多线程执行increase方法时,是否能保证它的值会是线性递增的呢?答案是否定的.原因:这里的increase方法,执行的操作是i++,即i=i+1;针对i=i+1,在多线程中的运算,本身需要改变i的值.如果,在i已从内存中取到最新值,但未与1进行运算,此时其他线程已数次将运算结

果赋值给i.则当前线程结束时,之前的数次运算结果都将被覆盖.即,执行100次increase,可能结果是<100.一般来说,这种情况需要较高的压力与并发情况下,才会出现.

如何避免这种情况?解决以上问题的方法:一种是操作时,加上同步.这种方法,无疑将大大降低程序性能,且违背了volatile的初衷.第二种方式是,使用硬件原语(CAS),实现非阻塞算法从CPU原语上,支持变量级别的低开销同步.CPU原语-比较并交换(CompareAndSet),实现非阻塞算法什么是CAS?cas是现代CPU提供给并发程序使用的原语操作.不同的CPU有不同的使用规范.在Intel处理器中,比较并交换通过指令的cmpxchg系列实现。PowerPC处理器有一对名为“加载并保留”和“条件存储”的指令,它们实现相同的目地;MIPS与PowerPC处理器相似,除了第一个指令称为“加载链接”。CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)什么是非阻塞算法?一个线程的失败或挂起不应该影响其他线程的失败或挂起.这类算法称之为非阻塞(non

blocking)算法对比阻塞算法:如果有一类并发操作,其中一个线程优先得到对象监视器的锁,当其他线程到达同步

边界时,就会被阻塞.直到前一个线程释放掉锁后,才可以继续竞争对象锁.(当然,这里的竞争也可是公平的,

按先来后到的次序)CAS原理:我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更

改该位置,只告诉我这个位置现在的值即可。CAS使用示例(jdk1.5并发包AtomicInteger类分析

/**

*Atomicallysetstothegivenvalueandreturnstheoldvalue.

*

*@paramnewValuethenewvalue

*@returnthepreviousvalue

*/publicfinalintgetAndSet(intnewValue){

for(;;){

intcurrent=get();

if(compareAndSet(current,newValue))

returncurrent;

}

}

publicfinalbooleancompareAndSet(intexpect,intupdate){

eAndSwapInt(this,valueOffset,expect,update);

}这个方法是,AtomicInteger类的常用方法,作用是,将变量设

置为指定值,并返回设置前的值.它利用了cpu原语compareAndSet来保障值的唯一性.另,AtomicInteger类中,其他的实用方法,也是基于同样的实现方式.比如getAndIncrement,getAndDecrement,getAndAdd等等.CAS语义上存在的"ABA问题"什么是ABA问题?假设,第一次读取V地址的A值,然后通过CAS来判断V地址的值是否仍旧为A,如

果是,就将B的值写入V地址,覆盖A值.但是,语义上,有一个漏洞,当第一次读取V的A值,此时,内存V的值变为B值,然

后在未执行CAS前,又变回了A值.此时,CAS再执行时,会判断其正确的,并进行赋值.这种判断值的方式来断定内存是否被修改过,针对某些问题,是不适用的.为了解决这种问题,jdk1.5并发包提供了AtomicStampedReference(有标记的原子引

用)类,通过控制变量值的版本来保证CAS正确性.其实,大部分通过值的变化来CAS,已经够用了.jdk1.5原子包介绍(基于volatile)包的特:1,普通原子数值类型AtomicInteger,AtomicLong提供一些原子操作的加减运算.2,使用了解决脏数据问题的经典模式-"比对后设定",即查看主存中数据是否与预期

提供的值一致,如果一致,才更新.3,使用AtomicReference可以实现对所有对象的原子引用及赋值.包括Double与Floa

t,但不包括对其的计算.浮点的计算,只能依靠同步关键字或Lock接口来实现了.4,对数组元素里的对象,符合以上特点的,也可采用原子操作.包里提供了一些数组原

子操作类AtomicIntegerArray,AtomicLongArray等等.5,大幅度提升系统吞吐量及性能.

本文详细介绍[JAVA100

]065

、线程同步

/**

*

Title:线程同步

*

Description:通过使用同步锁实现对共享数据的操作

*

Copyright:Copyright(c)2003

*

Filename:

*@version1.0

*/

/**

*
类说明:主程序

*

功能描述:构造两个线程,并启动它们

*/

publicclassSyThreadDemo

{

publicstaticvoidmain(String[]args)

{tradeft=newtrade();addThreadtt1=newaddThread(ft,"add");decThreadtt2=newdecThread(ft,"dec");();();

}

}

/**

*
类说明:同步类*
功能描述:保存共享数据,

*/

classtrade

{privateStringtransame;privatedoubleamount;

/**

*
方法说明:更新数据

*
输入参数:String

transame操作名称

*
输入参数:doubleamount资金数量

*
返回类型:

*/synchronizedvoidupdate(Stringtransame,doubleamount){ame=transame;=amount;n(ame+""+);}

}

/**

*
类说明:添加资金

*
功能描述:单线程,调用()方法,修改数据

*/

classaddThreadextendsThread

{privatetradeft;addThread

(tradeft,Stringname){super

(name);=ft;

}

publicvoidrun(){for(inti=0;i<10;i++)("add",2000.0);

}

}

/**

*
类说明:减少资金

*
功能描述:单线程,调用()方法,修改数据

*/

classdecThreadextendsThread

{privatetrade

fd;

decThread(tradefd,Stringname){super

(name);=fd;}

/**

*
方法说明:线程主体

*
输入参数:

*
返回类型:

*/publicvoidrun(){for(inti=0;i<10;i++)("dec",-2000.0);

}

}

-

JAVA高级多核线程volatile原理与技巧

发布时间:2022-04-14 10:27:40
文章版权声明:除非注明,否则均为IT技术网-学习WEB前端开发等IT技术的网络平台原创文章,转载或复制请以超链接形式并注明出处。

发表评论

评论列表 (有 5 条评论,656人围观)

最近发表

随便看看

热门文章

标签列表