zoukankan      html  css  js  c++  java
  • Java 8 锁机制

    主要对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.锁降级

    锁顺序:

    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.代码示例:

  • 相关阅读:
    XML错误信息Referenced file contains errors (http://www.springframework.org/schema/beans/spring-beans-4.0.xsd). For more information, right click on the message in the Problems View ...
    Description Resource Path Location Type Cannot change version of project facet Dynamic Web Module to 2.3.
    maven创建web报错Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:maven-compiler-plugin:maven-compiler-plugin:3.5.1:runtime Cause: error in opening zip file
    AJAX跨域
    JavaWeb学习总结(转载)
    JDBC学习笔记
    Java动态代理之JDK实现和CGlib实现
    (转)看懂UML类图
    spring boot配置使用fastjson
    python3下django连接mysql数据库
  • 原文地址:https://www.cnblogs.com/ljsong/p/8692970.html
Copyright © 2011-2022 走看看