Java 8 锁机制

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

 

1、synchronized

 

(一)、用法:java

1.synchronized能够用在方法(包含静态方法),api

2.synchronized块数据结构

void increment() {
  synchronized (this) {
    count += 1;
  }
}并发

 

(二)、原理:app

关键词:monitor高并发

1.synchronized修饰方法性能

synchronized void increment() {
  count += 1;
}this

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

 

2.synchronized修饰对象spa

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的全部权.

2、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.锁降级

锁顺序:

rwl.writeLock().lock();//先拿写锁
rwl.readLock().lock();//再拿读锁
rwl.writeLock().unlock();
rwl.readLock().unlock();

结论:

ReentrantReadWriteLock有锁降级机制(由于读锁是能够被多个线程共享的),若是一个线程拿到了写锁,那么它还能够继续拿到读锁.

 

(四)、StampedLock(时间戳锁)

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

2.代码示例:

3.解释:

所谓的乐观读模式,也就是若读的操做不少,写的操做不多的状况下,你能够乐观地认为,写入与读取同时发生概率不多,所以不悲观地使用彻底的读取锁定,程序能够查看读取资料以后,是否遭到写入执行的变动,再采起后续的措施,这一个小小改进,可大幅度提升程序的吞吐量.

4.和ReentrantReadWriteLock相比:

(1).一个线程状况下,读速度其4倍左右,写是1倍.
(2).六个线程状况下,读性能是其几十倍,写性能也是近10倍左右.

 

(五)、信号量

1.锁和信号量的区别:

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

2.代码示例:

相关文章
相关标签/搜索