zoukankan      html  css  js  c++  java
  • 并发编程004 --- Lock基本使用和原理

    前面提到,线程安全问题的源头有如下三个:

    1、缓存带来的可见性问题

    2、线程切换带来的原子性问题

    3、编译优化,指令重排,带来的顺序性问题

    其中1和3可以通过java提供的volatile关键字解决,而问题2的解决就需要借助java中的锁

    synchronized关键字

    java提供了synchronized关键字来实现线程的同步,synchronized关键字可以用来修饰代码块、普通方法和静态方法;修饰代码块时,必须指定锁变量,修饰普通方法时,默认锁变量为this,而修饰静态方法,锁变量默认为当前类的Class对象;并且synchronized是可重入的,即同一个线程能够多次获取到该锁

    ReentrantLock

    synchronized锁有如下的缺点:1、“很重” --- 值得商榷,因为JDK 1.6对其进行了优化;2、锁被其他线程占有时,只能等待,没有额外的尝试机制;因此引入了Lock接口类;

    1、lock()方法类似于synchronize关键字,如果线程尝试获取锁获取不到,线程会一直处于等待状态

    2、lockInterruptibly()方法更为灵活,如果锁获取不到,那么当前线程是可以响应中断的

    3、tryLock()方法,如果线程获取不到锁,那么会直接返回false,还提供了一个重载方法:tryLock(time ms),在指定时间内获取不到锁,返回false

    ReentrantLock实现了Lock接口,并且可以通过构造器的入参指定是否为公平策略:

    公平策略指的是:多线程竞争锁的时候,更倾向于将锁的持有权交由等待时间最长的线程;

    非公平策略指的是:多线程竞争锁的时候,由OS决定锁的持有权

    需要注意的是:一般情况下,多线程公平策略下,吞吐量比较低,线程执行开销大

    ReentrantReadWriteLock

    有这样一个场景,一个应用程序中,读取数据的次数远远大于写输入的次数,而读数据的线程间一定不会有线程安全问题,如果使用前面的两种锁,会造成锁的过度使用,因此引入了读写锁ReentrantReadWriteLock

    1、可重入读写锁有两个锁,读锁和写锁,读锁为共享锁,而写锁为独占锁

    2、检测到有线程持有写锁,线程无法获取到读锁,反之亦然,即读写互斥

    3、锁降级:线程A获取到写锁后,修改共享变量,然后获取读锁,接着释放写锁,然后对共享变量做其他的操作,完成后释放读锁;那么锁降级是否有必要呢?

          答案是肯定的,在前面叙述的场景下,在写线程释放后,另一个线程B修改了共享变量,会引入线程安全问题,因此在释放写锁前获取读锁,这样其他线程获取写锁时会等待读锁的释放

    StampedLock

    JDK1.8引入,前面的ReetrantReadWriteLock能够解决读多写少场景效率问题,但是容易引起写线程“饥饿”的问题,即:读线程过多,写线程一直无法获得写锁,一直无法得到真正的执行

    为了解决该问题,JDK1.8引入了StampedLock

    1、StampedLock有三种应用场景:读场景、写场景、乐观读场景,乐观读场景即:认为读期间,其他线程不会修改共享变量,这是一种乐观的行为,但是在一些情况下会有线程安全问题,因此需要用额外的手段解决该问题;

          官方提供了乐观读的例子,这里整理为伪代码模板,为了避免使用中出现问题,在乐观读场景下要严格遵循该模板

    long optStamped = lock.tryOptimisticRead();// 获取乐观读锁,实际只是获取邮戳,并未真正获取锁
    // 读取共享变量操作。。。
    if (!lock.validate(optStamped)) { // 其他线程做了写操作
          optStamped  = lock.readLock(); // 获取读锁
          try {
              // 重新读取共享变量
          } finally {
              lock.unReadLock(optStamped);
          }
    }

    2、需要注意:StampedLock是不可重入的

    3、支持读写锁相互转换

    4、无论读锁还是写锁,都不支持condition

         

  • 相关阅读:
    用变量构造函数检查变量类型
    HTML5的File API读取文件信息
    jQuery插件中的this指的是什么
    了解babel
    了解.gitignore
    高德地图画正六边形
    编写可维护性的js读书笔记
    百度地图遇到的问题
    实用的两个移动端demo
    git基本操作总结
  • 原文地址:https://www.cnblogs.com/sniffs/p/13522845.html
Copyright © 2011-2022 走看看