java8 n l_Java 8 锁机制 - osc_l745c1nv的个人空间 - OSCHINA - 中文开源技术交流社区

主要对Java 8 常用的锁如何使用进行分享

一、synchronized

(一)、用法:

1.synchronized可以用在方法(包含静态方法),

2.synchronized块

void increment() {

synchronized (this) {

count += 1;

}

}

(二)、原理:

关键词:monitor

1.synchronized修饰方法

synchronized void increment() {

count += 1;

}

反编译后会在方法的flag上带ACC_SYNCHRONIZED标记,当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象.

2.synchronized修饰对象

void increment() {

synchronized (this) {

count += 1;

}

}

反编译结果:

...

monitorenter

方法内逻辑(count += 1;)

monitorexit

...

monitorenter:

每个对象有一个monitor,当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

1.如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者.

2.如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权.

monitorexit:

执行monitorexit的线程必须是objectref所对应的monitor的所有者.

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个monitor的所有权.

二、Lock

(一)、与synchronized的区别

1.与synchronized不同的是,Lock完全用Java写成,在java这个层面是无关JVM实现的.Lock提供更灵活的锁机制,但因为lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}.

(二)、ReentrantLock(可重入互斥锁)

1.ReentrantLock是一个可重入的互斥锁,重入锁是一种递归无阻塞的同步机制.

2.ReentrantLock由最近成功获取锁,还没有释放的线程所拥有,当锁被另一个线程拥有时,调用lock的线程可以成功获取锁。如果锁已经被当前线程拥有,当前线程会立即返回.

代码示例:

ReentrantLock lock = new ReentrantLock();

if (lock.tryLock()) {//如果已经被lock,则立即返回false不会等待,达到忽略操作的效果

try {

//操作

} finally {

lock.unlock();

}

}

================

ReentrantLock lock = new ReentrantLock(true); //公平锁

lock.lock(); //如果被其它资源锁定,会在此等待锁释放,达到暂停的效果

try {

//操作

} finally {

lock.unlock();

}

================

ReentrantLock lock = new ReentrantLock(true); //公平锁

try {

if (lock.tryLock(5, TimeUnit.SECONDS)) {//如果已经被lock,尝试等待5s,看是否可以获得锁,如果5s后仍然无法获得锁则返回false继续执行

try {

//操作

} finally {

lock.unlock();

}

}

} catch (InterruptedException e) {//当前线程被中断时(interrupt),会抛InterruptedException

}

(三)、ReentrantReadWriteLock(可重入读写锁, 派生自ReadWriteLock, 单独实现, 和ReentrantLock没关系)

1.主要特性:

(1).重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想.

(2).WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持ReadLock的持有。反过来ReadLock想要升级为WriteLock则不可能.

(3).ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥。这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量.

代码示例(同一个线程中,在没有释放读锁的情况下,就去申请写锁,这属于锁升级,ReentrantReadWriteLock是不支持):

w.lock();//先获得写锁

try {

r.lock();//再获得读锁

try {

// do something

} finally {

r.unlock();

}

} finally {

w.unlock();

}

2.锁降级

image2017-3-10%2011%3A28%3A0.png?version=1&modificationDate=1489116480682&api=v2

锁顺序:

rwl.writeLock().lock();//先拿写锁

rwl.readLock().lock();//再拿读锁

rwl.writeLock().unlock();

rwl.readLock().unlock();

结论:

ReentrantReadWriteLock有锁降级机制(因为读锁是可以被多个线程共享的),如果一个线程拿到了写锁,那么它还可以继续拿到读锁.

(四)、StampedLock(时间戳锁)

1.它是java 8在java.util.concurrent.locks新增的一个API.

2.代码示例:

image2017-3-9%2016%3A55%3A12.png?version=1&modificationDate=1489051542775&api=v2

3.解释:

所谓的乐观读模式,也就是若读的操作很多,写的操作很少的情况下,你可以乐观地认为,写入与读取同时发生几率很少,因此不悲观地使用完全的读取锁定,程序可以查看读取资料之后,是否遭到写入执行的变更,再采取后续的措施,这一个小小改进,可大幅度提高程序的吞吐量.

4.和ReentrantReadWriteLock相比:

(1).一个线程情况下,读速度其4倍左右,写是1倍.

(2).六个线程情况下,读性能是其几十倍,写性能也是近10倍左右.

(五)、信号量

1.锁和信号量的区别:

锁是用于资源或者变量的互斥访问,而信号量是用来做"准入许可"

2.代码示例:

image2017-3-9%2017%3A24%3A35.png?version=1&modificationDate=1489051542738&api=v2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值