Java并发编程之ReentrantLock重入锁

简介: Java并发编程之ReentrantLock重入锁

ReentrantLock:



1.源码层面分析:


public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    //实现同步逻辑的同步器,提供了所有锁相关的操作
    private final Sync sync;
    //默认构造函数,提供了非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    /**
     * 根据策略(公平,非公平)来构建重入锁
     * @param fair true表示采用公平锁,false表示采用非公平锁
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    //获取锁,获取成功则返回,否则阻塞获取线程直到获取成功
    public void lock() {
        sync.lock();
    }
     //获取锁,获取成功则返回,否则阻塞获取线程直到获取成功;并响应中断,也就是说获取线程被Thread.interrupt()中断后,该方法会抛出InterruptedException并返回。
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    /** 
     * 尝试获取锁,不管成功,失败都立即返回,不会阻塞
     * @return true表示获取成功,false表示失败
     */       
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    /** 
     * 尝试获取锁,获取成功则返回,否则等待timeout时间返回
     * @param timeout 等待获取锁的超时时间
     * @param unit 时间单位
     * @return true表示获取成功,false表示超时时间到达仍然获取失败 
     */
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
    //释放锁,该方法不会阻塞
    public void unlock() {
        sync.release(1);
    }
    /** 
     * 获取一个条件对象
     * @return 条件对象
     */   
    public Condition newCondition() {
        return sync.newCondition();
    }


2.Sync内部类:


//同步抽象类,基于AQS实现
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;
    //具体由子类NoFairSync,FairSync实现
    abstract void lock();
    /**
         * 非公平的方式尝试获取资源
         * @param acquires 资源个数
         * @return true表示获取成功,false获取失败
         */
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        //获取资源状态,volatile读
        int c = getState();
        //0表示初始状态或者锁释放状态,可以直接获取资源
        if (c == 0) {
            //CAS判断资源获取是否成功
            if (compareAndSetState(0, acquires)) {
                //获取成功则设置当前线程为资源(锁)的持有线程
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //如果当前线程为持有锁的线程,可以直接操作资源,体现重入性
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        //其他状态说明资源已被其他占用无法获取
        return false;
    }
    /**
         * 尝试释放资源
         * @param releases 资源个数
         * @return true表示释放成功,false释放失败
         */
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        //如果当前线程不为持有资源的线程,则抛出异常
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        //如果释放后,资源的个数为0表示释放成功
        if (c == 0) {
            free = true;
            //设置资源的独占线程为null,此时没有线程占用资源
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    /**
         * 判断当前线程是否持有资源
         * @return true表示是,false表示否
         */
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }
    /**
         * 获取条件对象
         * @return 新的条件对象
         */
    final ConditionObject newCondition() {
        return new ConditionObject();
    }
    /**
         * 获取持有资源的线程
         * @return 持有资源的线程
         */
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }
    /**
         * 获取持有资源的个数
         * @return 持有资源的个数
         */
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }
    /**
         * 获取资源是否被线程占用
         * @return true表示是,false否
         */
    final boolean isLocked() {
        return getState() != 0;
    }
}


ReentrantLock内部基于Sync提供了非公平锁和公平锁的实现。


3.非公平锁


//非公平锁继承自Sync
 static final class NonfairSync extends Sync {
     private static final long serialVersionUID = 7316153563782823691L;
     /**
         * 非公平锁提供的锁定方法
         */
     final void lock() {
         //先尝试CAS获取操作,获取成功则设置当前线程为资源持有线程并返回
         if (compareAndSetState(0, 1))
             setExclusiveOwnerThread(Thread.currentThread());
         else
             //获取失败则进入AQS的acquire逻辑,排队获取资源
             acquire(1);
     }   
     /**
         * 非公平的方法尝试获取资源,AQS对外提供的需要实现方法
         * @param acquires资源个数
         * @return true表示是,false否
         */
     protected final boolean tryAcquire(int acquires) {
         //用Sync类的非公平方式尝试获取资源
         return nonfairTryAcquire(acquires);
     }
 }


4.公平锁:


static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
    /**
         * 公平锁提供的锁定方法
         */
    final void lock() {
        //排队获取资源,体现公平性
        acquire(1);
    }
    /**
         * 公平的方式尝试获取资源
         * @param acquires资源个数
         * @return true表示是,false否
         */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        //0表示当前可以直接获取资源
        if (c == 0) {
            //先判断当前有没有线程排队获取,如果没有当前线程才能获取,体现公平性
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //如果当前线程本来就持有资源,可以继续操作资源,体现重入性
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        //其他情况获取失败,让线程排队获取
        return false;
    }
}


5.**总结:**非公平主要体现在资源获取时,如果获取线程发现当前资源可以获取成功(其他线程释放了资源)那就直接获取返回,而此时可能有其他线程先于当前线程在排队获取,那么对于这部分线程来说是“不公平”的,没有体现先到先得的原则。反之,就是公平锁,先排队的线程先获取锁,不存在插队的情况,体现了公平性,所以叫公平锁。


6.ReentrantLock默认使用非公平方式提供锁的获取和释放,可以看到相比公平模式,非公平模式获取锁的效率更高,因为在非公平模式下如果能够获取锁成功则线程不需要再排队,而在公平模式下,所有线程都必须排队获取锁,所以从当前获取锁的线程的角度非公平模式效率更高。还有一个地方需要说明的是:ReentrantLock是可重入锁,也就是说同一个线程获取锁后可再次重复获取锁无需阻塞。


7.典型应用场景:


class X {
   private final ReentrantLock lock = new ReentrantLock();
   private Condition condition = lock.newCondition(); 
   public void m() {
     lock.lock();  // 获取锁
     try {
           try {
                //必须先获取锁才能执行条件等待,其他线程需要执行condition.signal()方法,来唤醒等待线程
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        // 执行具体业务方法
      } finally {
        lock.unlock() //释放锁,必须在finally块中,防止发生异常时未执行释放锁操作,导致其他线程无法获取锁
      }
    }
 }}


目录
相关文章
|
9天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
8天前
|
Java 开发者
Java多线程编程的艺术与实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的技术文档,本文以实战为导向,通过生动的实例和详尽的代码解析,引领读者领略多线程编程的魅力,掌握其在提升应用性能、优化资源利用方面的关键作用。无论你是Java初学者还是有一定经验的开发者,本文都将为你打开多线程编程的新视角。 ####
|
7天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
10天前
|
安全 Java 开发者
Java多线程编程中的常见问题与解决方案
本文深入探讨了Java多线程编程中常见的问题,包括线程安全问题、死锁、竞态条件等,并提供了相应的解决策略。文章首先介绍了多线程的基础知识,随后详细分析了每个问题的产生原因和典型场景,最后提出了实用的解决方案,旨在帮助开发者提高多线程程序的稳定性和性能。
|
13天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
5月前
|
Java C++
关于《Java并发编程之线程池十八问》的补充内容
【6月更文挑战第6天】关于《Java并发编程之线程池十八问》的补充内容
49 5
|
2月前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
4月前
|
安全 Java 开发者
Java中的并发编程:深入理解线程池
在Java的并发编程中,线程池是管理资源和任务执行的核心。本文将揭示线程池的内部机制,探讨如何高效利用这一工具来优化程序的性能与响应速度。通过具体案例分析,我们将学习如何根据不同的应用场景选择合适的线程池类型及其参数配置,以及如何避免常见的并发陷阱。
57 1
|
4月前
|
监控 Java
Java并发编程:深入理解线程池
在Java并发编程领域,线程池是提升应用性能和资源管理效率的关键工具。本文将深入探讨线程池的工作原理、核心参数配置以及使用场景,通过具体案例展示如何有效利用线程池优化多线程应用的性能。
|
3月前
|
Java 数据库
Java中的并发编程:深入理解线程池
在Java的并发编程领域,线程池是提升性能和资源管理的关键工具。本文将通过具体实例和数据,探讨线程池的内部机制、优势以及如何在实际应用中有效利用线程池,同时提出一个开放性问题,引发读者对于未来线程池优化方向的思考。
43 0
下一篇
无影云桌面