`
hujin1979
  • 浏览: 80706 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

可重入的互斥锁--ReentrantLock

阅读更多

    一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

 

   ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread()getHoldCount() 方法来检查此情况是否发生。

 

    此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。与采用默认设置(使用不公平锁)相比,使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁和保证锁分配的均衡性时差异较小。不过要注意的是,公平锁不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。还要注意的是,未定时的 tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁是可用的,此方法就可以获得成功。

 

   以上是SUN对该对象给出的解释。以下是转载内容:

  

它提供了lock()方法:
如果该锁定没有被另一个线程保持,则获取该锁定并立即返回,将锁定的保持计数设置为 1。
如果当前线程已经保持该锁定,则将保持计数加 1,并且该方法立即返回。
如果该锁定被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁定之前,该线程将一直处于休眠状态,此时锁定保持计数被设置为 1。

 

  

Java concurrent中关于任务调度的实现时,读了延迟队列DelayQueue的一些代码,比如take()。该方法的主要功能是从优先队列(PriorityQueue)取出一个最应该执行的任务(最优值),如果该任务的预订执行时间未到,则需要wait这段时间差。反之,如果时间到了,则返回该任务。而offer()方法是将一个任务添加到该队列中。

后来产生了一个疑问:如果最应该执行的任务是一个小时后执行的,而此时需要提交一个10秒后执行的任务,会出现什么状况?还是先看看take()的源代码:

  

 而以下是offer()的源代码:

 

public boolean offer(E e) {
                final ReentrantLock lock = this.lock;
                lock.lock();
                try {
                    E first = q.peek();
                    q.offer(e);
                    if (first == null || e.compareTo(first) < 0)
                        available.signalAll();
                    return true;
                } finally {
                    lock.unlock();
                }
            } 

 

    如代码所示,take()和offer()都是lock了重入锁。如果按照synchronized的思维(使用诸如synchronized(obj)的方法),这两个方法是互斥的。回到刚才的疑问,take()方法需要等待1个小时才能返回,而offer()需要马上提交一个10秒后运行的任务,会不会一直等待take()返回后才能提交呢?答案是否定的,通过编写验证代码也说明了这一点。这让我对重入锁有了更大的兴趣,它确实是一个无阻塞的锁。

下面的代码也许能说明问题:运行了4个线程,每一次运行前打印lock的当前状态。运行后都要等待5秒钟。 

public static void main(String[] args) throws InterruptedException {
              final ExecutorService exec = Executors.newFixedThreadPool(4);
              final ReentrantLock lock = new ReentrantLock();
              final Condition con = lock.newCondition();
              final int time = 5;
              final Runnable add = new Runnable() {
                public void run() {
                  System.out.println("Pre " + lock);
                  lock.lock();
                  try {
                    con.await(time, TimeUnit.SECONDS);
                  } catch (InterruptedException e) {
                    e.printStackTrace();
                  } finally {
                    System.out.println("Post " + lock.toString());
                    lock.unlock();
                  }
                }
              };
              for(int index = 0; index < 4; index++)
                exec.submit(add);
              exec.shutdown();
            }

 

 

这是它的输出:
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Unlocked]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-2]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-3]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-4]

每一个线程的锁状态都是“Unlocked”,所以都可以运行。但在把con.await改成Thread.sleep(5000)时,输出就变成了:
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-2]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-3]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-4]

 

    以上的对比说明线程在等待时(con.await),已经不在拥有(keep)该锁了,所以其他线程就可以获得重入锁了。

有必要会过头再看看Java官方的解释:“如果该锁定被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁定之前,该线程将一直处于休眠状态”。我对这里的“保持”的理解是指非wait状态外的所有状态,比如线程Sleep、for循环等一切有CPU参与的活动。一旦线程进入wait状态后,它就不再keep这个锁了,其他线程就可以获得该锁;当该线程被唤醒(触发信号或者timeout)后,就接着执行,会重新“保持”锁,当然前提依然是其他线程已经不再“保持”了该重入锁。

 

    总结一句话:对于重入锁而言,"lock"和"keep"是两个不同的概念。lock了锁,不一定keep锁,但keep了锁一定已经lock了锁。

public E take() throws InterruptedException {
                final ReentrantLock lock = this.lock;
                lock.lockInterruptibly();
                try {
                    for (;;) {
                        E first = q.peek();
                        if (first == null) {
                            available.await();
                        } else {
                            long delay =  first.getDelay(TimeUnit.NANOSECONDS);
                            if (delay > 0) {
                                long tl = available.awaitNanos(delay);
                            } else {
                                E x = q.poll();
                                assert x != null;
                                if (q.size() != 0)
                                    available.signalAll(); // wake up other takers
                                return x;
                            }
                        }
                    }
                } finally {
                    lock.unlock();
                }
            } 

 

分享到:
评论

相关推荐

    各种锁汇总,乐观锁、悲观锁、分布式锁、可重入锁、互斥锁、读写锁、分段锁、类锁、行级锁等

    本文将深入探讨标题和描述中提及的各种锁,包括乐观锁、悲观锁、分布式锁、可重入锁、互斥锁、读写锁、分段锁、类锁以及行级锁。 1. **乐观锁**:乐观锁假设多线程环境中的冲突较少,所以在读取数据时不加锁,只有...

    Java多线程 ReentrantLock互斥锁详解

    Java多线程ReentrantLock互斥锁详解 ReentrantLock是Java多线程编程中的一种锁机制,它可以实现线程之间的同步访问资源。ReentrantLock的主要特点是可以重入,即一个线程可以多次获得锁,而不需要释放锁。这种机制...

    Java多线程之ReentrantLock与Condition - 平凡希 - 博客园1

    Java中的`ReentrantLock`是Java并发包`java.util.concurrent.locks`中的一个高级锁机制,它是可重入的互斥锁,具有与`synchronized`关键字相似的同步性,但提供了更多的灵活性和控制功能。本篇文章将深入探讨`...

    Java concurrency之互斥锁_动力节点Java学院整理

    总结来说,`ReentrantLock`是Java并发编程中用于实现互斥锁的重要工具,提供可重入性、公平性和额外的控制功能,帮助开发者编写更安全、高效的多线程程序。在处理高并发场景或需要更高级别控制时,使用`...

    ReentrantLock与synchronized

    - 它是可重入的,即一个线程可以多次获取同一锁,不会发生死锁。 2. **使用方式**: - `ReentrantLock`提供`lock()`和`unlock()`方法来加锁和解锁。 - 支持公平锁(通过构造函数传入`true`)和非公平锁(默认)...

    偏向锁-轻量级锁-重量级锁

    **重量级锁**是基于互斥量(Mutex)实现的,它是最传统的锁机制,也是最不高效的。当轻量级锁升级为重量级锁时,会阻塞后来的线程,直到持有锁的线程释放锁。这种锁的开销主要在于线程上下文切换,对于高并发的场景...

    详解Java多线程编程中互斥锁ReentrantLock类的用法

    1. **可重入性**:ReentrantLock允许一个线程在已经获取锁的情况下再次获取该锁,而不会发生死锁。这对于递归方法或者嵌套同步块特别有用。例如,当一个线程已经获得了锁,然后在同步代码块内部调用了另一个需要相同...

    第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()1

    ReentrantLock是由Java提供的可重入互斥锁,支持公平锁和非公平锁两种模式。非公平锁的特性是获取锁的线程不论先后,都有机会获得锁,而公平锁则遵循先进先出的原则,保证线程按照请求锁的顺序进行。 2. **...

    Java concurrency之互斥锁_动力节点Java学院

    `ReentrantLock`是一个可重入的互斥锁,这意味着一个线程可以多次获取同一把锁,即在获取锁后,线程可以再次请求同一把锁而不被阻塞。这种设计允许在复杂的同步逻辑中避免死锁的发生。`ReentrantLock`有两种模式:...

    ReentrantLock解析

    在Java并发编程中,ReentrantLock是JDK提供的一个可重入互斥锁,它是java.util.concurrent.locks包下的核心类。与synchronized关键字相比,ReentrantLock提供了更高的灵活性,如尝试加锁、定时加锁和公平锁等功能。...

    基于consul的分布式锁工具,包含:互斥锁、信号量等工具

    - **互斥锁**:互斥锁确保同一时间只有一个线程可以访问资源,其他试图获取锁的线程必须等待锁被释放。在Java中,通常通过`java.util.concurrent.locks.ReentrantLock`实现。 - **信号量**:信号量允许多个线程...

    Java多线程并发编程(互斥锁Reentrant Lock)

    2. 可重入锁:ReentrantLock 是可重入锁,即某一线程可以多次获得该锁。 3. 公平锁和非公平锁:ReentrantLock 可以选择实例化一个公平锁或非公平锁,默认情况下会构造一个非公平锁。公平锁可以确保有序性(FIFO 队列...

    Java 中15种锁的介绍

    Java的`ReentrantLock`和`synchronized`都是可重入锁。 - **不可重入锁** 如果线程已经在持有锁的情况下尝试再次获取,会导致死锁。例如,上述代码中的自旋锁模拟展示了不可重入锁的实现。 3. **独享锁 / 共享锁**...

    Java使用synchronized实现互斥锁功能示例

    Java使用synchronized实现互斥锁功能示例 在Java编程语言中,synchronized关键字是实现互斥锁功能的主要手段。互斥锁是一种机制,用于控制多个线程访问共享资源的顺序,从而避免因为资源竞争而导致的程序错误。在...

    多线程(11)ReentrantLock公平锁与非公平锁(修改)1

    在Java并发编程中,`ReentrantLock`是一个可重入的互斥锁,相比`synchronized`提供了更细粒度的控制以及更丰富的特性。`ReentrantLock`的公平性和非公平性是其重要的特性之一。在上述代码中,我们创建了一个`...

    Java锁的种类以及区别

    - `ReentrantLock`就是一种典型的可重入锁。 - `Synchronized`也是可重入锁的一种实现。 #### 三、独享锁与共享锁 **1. 独享锁** 独享锁也称为排他锁,指的是在某个时间段内只允许一个线程获取锁,这意味着如果...

    【并发编程】简单化理解AQS和ReentrantLock.pdf

    - **ReentrantLock**:是一个可重入的互斥锁,提供了比`synchronized`关键字更丰富的功能。 - **特点**: - 支持公平锁和非公平锁。 - 可以通过tryLock尝试获取锁,增加灵活性。 - 提供更精细的锁释放控制,避免...

    JavaMutex:提供100%纯Java互斥。-开源

    JavaMutex库不仅提供了基本的互斥锁,还扩展了这一概念,提供了读写锁、只读重入锁、条件变量等高级同步工具。 读写锁允许多个线程同时读取共享资源,但对写操作进行独占,这样可以提高多线程环境下的并发性能。...

    Java中的锁分类与使用.docx

    - 可重入锁允许同一个线程多次获取同一把锁,例如Java的`synchronized`关键字和ReentrantLock。这种特性避免了死锁的发生,使得线程在嵌套调用中可以自由地进入和退出同步块。 5. **公平锁/非公平锁** - **公平锁...

Global site tag (gtag.js) - Google Analytics