并发总结(2)

[url=http://reilost.iteye.com/blog/666826]并发总结(1)[/url]

3.线程的安全.

上面提到过同步机制来协调访问,防止风险.在Java中首要的同步机制是synchronized关键字,提供了独占锁,除此外还有volatile,jdk5里加入的显示锁lock,reentrantLock和原子变量.


在说这些之前呢,不得不提一下,jvm内的同步机制,jvm使用的同步机制是等待并唤醒监视器.

3.1 监视器

监视器支持互斥和写作.jvm通过对线锁实现互斥,让多个线程在同一个共享数据上独立且不影响其他线程的进行工作.协作则是通过Object类的wait和notify方法来实现的,允许多个线程为了一个目标而一起工作.
如图--.--.....
[img]http://reilost.com/blog/wp-content/uploads/2010/05/2.jpg[/img]

这个图里展示了线程与监视器交互的过程.
如果一个线程到达监视的时候,他会先进入入口区,这时候如果没有线程,则会直接进入下阶段然后持有监视器,然后执行任务.
反之,如果有线程在持有,新来的就会在入口区等待.
在持有中的线程,有两种情况,一种是执行完毕,则释放监视器然后退出.而执行了等待的命令的会进入释放后进入右边的等待区.这时候,如果下一个执行线程执行完毕后,没有执行唤醒,而且这之前也没有等待中的线程被唤醒,那入口区的线程会竞争监视器的持有权.获胜的会进入持有.
如果有唤醒,入口区与等待区就要一起竞争了...
一个线程只有在他持有监视器时才能执行等待命令,而且只有再一次持有监视器后才能离开等待区.

jvm里提供了两种唤醒命令,notify和notifyall..
notify随意从等待区选择一个线程并将其标志为可能唤醒,notifyall会把所有等待区的线程标识为可能唤醒..

3.2 对象锁

jvm对每一个对象和类都加了一个锁.如果线程获取了锁,在他释放前,没有其它线程可以获取同样数据的锁了..
这里要说下哈,线程共享使用一部分数据,另外的数据是被线程私有的..共享的数据是堆和方法区里的数据.也就是说访问,在堆里的实例变量和在方法区里的类变量是要协调线程间数据共享的.而在栈中的局部变量,是属于拥有该栈的线程私有的.

一个对象的关联的监视器会保护对象的实例变量,
一个类的关联的监视器会保护类的类变量.
如果对象没有是咧,类没有类变量,那么关联的监视器就什么都不监视了.

还有,jvm加载一个class的时候会创建一个Class类的实例,所以类锁也就是对象锁的一种实现,他锁的是那个类的class对象.

3.3
在Java代码里,有两种可以监视的区域,一个是语句,一个是方法..当一个线程到达这个区域第一条指令的时候,就要加锁,获得了锁以后,就会进入代码开始执行,当离开代码的时候,无论如何离开,相关的锁都会释放.

随便写个同步语句的..
	public  int getValue(){
synchronized (this) {
value++;
}
return value;
}


嗯.看下字节码..这里要说下,同步语句用的操作码是MONITORENTER和MONITOREXIT
public getValue()I
TRYCATCHBLOCK L0 L1 L2
TRYCATCHBLOCK L2 L3 L2
L4
LINENUMBER 6 L4
ALOAD 0
DUP
ASTORE 1
MONITORENTER
L0
LINENUMBER 7 L0
ALOAD 0
DUP
GETFIELD com/reilost/thread/GetSequence.value : I
ICONST_1
IADD
PUTFIELD com/reilost/thread/GetSequence.value : I
L5
LINENUMBER 6 L5
ALOAD 1
MONITOREXIT
L1
GOTO L6
L2
ALOAD 1
MONITOREXIT
L3
ATHROW
L6
LINENUMBER 10 L6
ALOAD 0
GETFIELD com/reilost/thread/GetSequence.value : I
IRETURN
L7
LOCALVARIABLE this Lcom/reilost/thread/GetSequence; L4 L7 0
MAXSTACK = 3
MAXLOCALS = 2


然后看下同步这个方法
public synchronized int getValue(){
value++;
return value;
}


把关键字加在方法上会有啥样滴变化呢.-.-..看字节码
  public synchronized getValue()I
L0
LINENUMBER 6 L0
ALOAD 0
DUP
GETFIELD com/reilost/thread/GetSequence.value : I
ICONST_1
IADD
PUTFIELD com/reilost/thread/GetSequence.value : I
L1
LINENUMBER 7 L1
ALOAD 0
GETFIELD com/reilost/thread/GetSequence.value : I
IRETURN
L2
LOCALVARIABLE this Lcom/reilost/thread/GetSequence; L0 L2 0
MAXSTACK = 3
MAXLOCALS = 1


对比一下就会发现,区别就是这里没有使用任何特别的操作码.这段代码没有进入和离开监视器的代码.因为同步方法的时候,方法不包含用于保存加锁对象的局部变量,所以方法每次用的局部变量的位置都是不固定的..当执行这个同步方法的时候,jvm会在调用方法前就获取一个锁,如果是实例方法,就取得对象关联的锁,类方法的话取得是类的锁,就是这个类的Class对象的缩.,执行结束后,会释放这个锁..

嗯..大家看下第一个同步语句字节码在推出监视的时候,有一个athrow
 MONITOREXIT
L3
ATHROW

这个是jvm加上的,作用是来保证锁被释放.不管如果同步语句如何退出,锁总是会被释放..
而在同步方法里并没有这样一个操作过程..

还有就是静态方法的时候,线程获得的是Class实例的锁.

............我果然不擅长码字啊...orz....写了这么多废话竟然-.-....我听从@EricChaw同学的...明天继续码...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值