java并发你也许不知道的(读Java Concurrency in Practice)

[size=large]1.线程安全[/size]
a.无状态的类是线程安全的
b.所有状态都具有原子性的类是线程安全的
原子性:即对该类的操作是不被打断的,即使在多线程的环境下
如果一个类只有单个状态,推荐使用jdk中的java.util.concurrent.atomic包AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicIntegerFieldUpdater
AtomicLong
AtomicLongArray
AtomicLongFieldUpdater
AtomicMarkableReference
AtomicReference
AtomicReferenceArray
AtomicReferenceFieldUpdater
AtomicStampedReference
这些类作为状态都具有原子性
[size=large]2.Synchronized[/size]
a.如果一个方法用Synchronized关键字修饰,则调用该方法所用的锁,为调用方法的对象的固有锁(如反射),如果同时带有Static关键字修饰,则使用的锁为该方法所在的class对象的固有锁。
固有锁:每个对象都隐性地带有一个锁,该锁为重入锁(重入锁将在后文介绍),用于synchronized关键字
像Vector,HashTable这样的类,其实就是用了类的固有锁,这该锁限制每个该类的方法都必须先获得该锁,才能继续执行

[size=large]3.重入锁[/size]
关于重入锁的解释用书中英文的解释会更准确:But because intrinsic locks are reentrant, if a thread tries to acquire a lock that it already holds, the request succeeds.

可能看完上面的还是有点难理解
举一个书中的例子:
看以下代码
[img]http://dl.iteye.com/upload/attachment/290907/1a0363bf-5bc2-3f68-b6b7-8cdeb39cc126.jpg[/img]
假设不是可重入锁,那会出现一个什么现象?
调用super.doSomething()时发现该方法被锁住了,然后等待,但等待的锁自己正持有着,也就变成了死锁

重入锁的实现:
重入锁是通过一个count和记录owner--一个持有该锁的Thread来实现的。当count值为0,认为这个锁当前没被任何线程持有。当一个线程持有了该锁了,jvm就会把owner置为该线程,并把count置为1,如果同样锁线程再次请求该锁,那jvm就会把count+1,如果线程退出同步块,则count-1,当count为0时,锁就释放了。

[size=large]4.线程状态的锁操作[/size]
对于每一个涉及到多个变量的不变量,所有涉及到的变量都应该用同一个锁来进行同步
怎么理解呢?
看例子:
[img]http://dl.iteye.com/upload/attachment/292974/75077c21-9a01-33f3-8d77-013ab49aae67.jpg[/img]
在vector中,每个方法都是用synchronized修饰的,但在上面的使用中--如果两个线程先后进入该语句块,再轮流执行vector.contain(element),显然还是会添加两次同样的element到vector中,所以还是不能保证vector的原子性

[size=large]5.程序中的代码是可能被打乱的[/size]
如果没有进行同步,编译器,进程,和运行环境将有可能会打乱运行的顺序。
如文中所举例子:
[img]http://dl.iteye.com/upload/attachment/292981/33837d3b-35b0-3638-99c8-182f00befe46.jpg[/img]
将有可能出现这样的状况:输出结果为0.
这是因为执行的顺序被打乱了

[size=large]6.64位数据的读或写是非原子性的[/size]
一般的数据类型,如int,执行
int a = 1;
int b = a;
这些操作都是原子操作,但是像long,double就不是了
jvm是允许把64位数据的读和写分开两次操作的,每次操作32位。
因此,有些项目,我们对某些数据的实时性不会太在意,就不会定义该变量为原子变量,但如果是long或者double类型,就要小心了。很有可能某用户在读取的时候读到的是前一个数据的前32位,和后一个数据的后32位

[size=large]7.volatile关键字[/size]
当一个变量用volatile声明了,编译器会放一个notice到该变量的共享区,然后当执行的时候,就不会对这个变量在程序中的执行顺序进行修改。并且volatile不会缓存在寄存器中,也不会缓存在别的进程中,因此,读volatile修饰的变量时总能返回一个最新更新的值。

使用锁能同时保证变量的可见性和原子性,但volatile只能保证其可见性。

什么时候不适合使用volatile?
举一个经典的例子:i++操作,在并发环境下volatile是不能保证其正确性的

什么时候可以用volatile?
* 该变量的值不依赖该变量当前的值,像i++,或者你能保证该变量的更新只在单线程的环境下出现
* 该变量不和其他的变量一起包含在一个不变量中,如上文的vector
* 由于其他的原因,该变量不需要使用锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值