zoukankan      html  css  js  c++  java
  • Java并发编程:Lock

    Lock概述

    • 位于java.util.concurrent.locks包内
    • 主要目的是和synchronized一样,两者都是为了解决同步问题,处理资源争端而产生的技术 

    java.util.concurrent.locks包下常用的类

    Lock

    lock为一个接口,主要方法如下

    lock(),tryLock(),tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁。

    unLock()方法用来释放锁。

    newCondition()用来实现类似wait(),notify()。

    lock()方法算是使用频率最高的一个方法,就是用来获取锁。如果锁被其他线程获取,则等待。因为发生异常锁也不会被释放,所以lock必须在try{}catch{}块中进行,并且在finally块中释放锁,防止死锁发生,一般使用方式如下:

    Lock lock = new ReentrantLock();
    lock.lock();
    try{
        // 处理任务
    }catch(Exception e){
         
    }finally{
        lock.unlock();   //释放锁
    }

    tryLock()方法,尝试获取锁,如果获取成功,返回true,获取失败(被其他线程占用)则返回false,立即返回,不会等待

    tryLock(long time, TimeUnit unit)和tryLock方法类似,区别在于获取锁的时候指定等待一定的时间,如果过了这个时间还没有获取锁,则返回false,一般情况下使用方式如下:

    Lock lock = new ReentrantLock();
    if(lock.tryLock()) {
         try{
             //处理任务
         }catch(Exception ex){
             
         }finally{
             lock.unlock();   //释放锁
         } 
    }else {
        //如果不能获取锁,则直接做其他事情
    }

    lockInterruptibly()方法获取锁的时候,如果线程正在等待获取锁,则中断这个线程的等待状态。打个比方,两个线程同时通过lock.lockInterruptibly()方法获取锁时,如果线程A获取了锁,线程B只能等待,那么对线程B调用b.interrupt()方法能够终端线程B的等待过程,使用方式如下

    public void method() throws InterruptedException {
      //如果需要正确中断等待锁的过程,必须将锁放在try外面,然后将异常抛出
      lock.lockInterruptibly(); 
        try {  
         //.....
        }
        finally {
            lock.unlock();
        }  
    } 

    注意:当一个线程获取锁之后,是不会被interrupt()方法中断,只能中断阻塞过程中的线程,所以当通过lockInterruptibly()方法获取某个锁的时候,如果不能获取到,只有在等待的情况下,才可以响应中断

    而使用synchronized修饰的话,当一个线程处理等待获取锁的状态,是无法被中断的,只有一只等待下去

    ReentrantLock

    为可重入锁。可重入的意义在于持有锁的线程可以继续持有,并且要释放对等的次数后才真正释放该锁。意味着线程可以进入它已经拥有的锁的同步代码块儿。

    lock一般声明为成员变量,局部变量的话,属于每次方法调用产生的实例,调用lock()方法自然获取的是不同的锁

    1. 先new一个实例
      static ReentrantLock r=new ReentrantLock();
    2. 加锁
      r.lock()或r.lockInterruptibly();
      此处不同,后者可被打断。当a线程lock后,b线程阻塞,此时如果是lockInterruptibly,
      那么在调用b.interrupt()之后,b线程退出阻塞,并放弃对资源的争抢,进入catch块。(如果使用后者,必须throw interruptable exception或catch)
    3. 释放锁
      r.unlock();
      必须做!要放在finally里面。以防止异常跳出了正常流程,导致灾难。(哪怕发生了OutofMemoryError,finally块中的语句执行也能够得到保证)

    其他一些方法

    isFair() // 是否公平锁

    isLocked() // 是否被任何线程获取了

    isHeldByCurrentThread() // 是否被当前线程获取了

    hasQueuedThreads() // 是否有线程在等待该锁

    ReadWriteLock

    也是一个接口,顾名思义为读写锁,声明如下

    将读写分离,变成了两把锁,从而形成读写不互斥

    ReentrantReadWriteLock

    是ReadWriteLock的具体实现

    • 可重入读写锁(读写锁的一个实现)
      ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
      ReadLock r = lock.readLock();
      WriteLock w
      = lock.writeLock();
    • 两者都有lock,unlock方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码

    Lock和synchronized的区别

    其实本质上两则是一样的,只不过lock更加强大

    • Lock是jdk中一组类库,synchronized是Java语言的关键字,属于语言的特性
    • 提供读写锁,公平锁
    • lock可以知道有没有获取锁,synchronized不行
    • synchronized发生异常时,会自动释放线程占有的锁,而Lock必须主动通过unLock()来释放,不然可能造成死锁,因此使用unLock()方法需要放在finally块中
    • lock可以让等待锁的线程中断,synchronized不行,需要一直等待

    至于两者之间的选择,对并发量大,资源竞争激烈的场景,使用Lock下面的类库性能还是不错的,并发量小,两者差不多,synchronized足矣

    《Java并发编程实践》一书给出了使用 ReentrantLock的最佳时机:

    当你需要以下高级特性时,才应该使用:可定时的、可轮询的与可中断的锁获取操作,公平队列,或者非块结构的锁。否则,请使用synchronized。     

    还有一点,我觉得synchronized可以指定获取哪一把对象锁,这个还是挺实用的

    心里有束光,眼里有片海
  • 相关阅读:
    leetcode 334. Increasing Triplet Subsequence
    leetcode 235. Lowest Common Ancestor of a Binary Search Tree
    leetcode 459. Repeated Substring Pattern
    python爬虫之Xpath
    python爬虫之bs4 美丽汤
    python3爬虫的模拟浏览器
    python爬虫之requests库
    ModelForm:表单中的 Field 和模型中的 Field重复解决
    django中关于表单自定义验证器和常用验证器
    关于http连接的本质 已经cookies和session
  • 原文地址:https://www.cnblogs.com/xhy-shine/p/8645366.html
Copyright © 2011-2022 走看看