`
hujin1979
  • 浏览: 78689 次
  • 性别: 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();
                }
            } 

 

分享到:
评论

相关推荐

    Java多线程 ReentrantLock互斥锁详解

    主要介绍了Java多线程 ReentrantLock互斥锁详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

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

    Java多线程并发的程序中使用互斥锁有synchronized和ReentrantLock两种方式,这里我们来详解Java多线程编程中互斥锁ReentrantLock类的用法:

    locks框架_ReentrantLock.pdf

    解释为什么它被称为“可重入锁”,以及如何解决传统锁可能的问题。 ReentrantLock 的基本用法: 深入探讨如何使用 ReentrantLock 来保护共享资源。演示如何通过 lock 和 unlock 方法来实现线程的同步和互斥。 ...

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

    主要介绍了ReentrantLock 互斥锁,在同一时间只能被一个线程所占有,在被持有后并未释放之前,其他线程若想获得该锁只能等待或放弃,需要的朋友可以参考下

    高级开发并发面试题和答案.pdf

    ReentrantLock如何实现可重入性 volatile作用; wait 与 sleep 的有什么不同?回答的要点四个: Thread.sleep()和LockSupport.park()的区别 Object.wait()和LockSupport.park()的区别 线程和线程池 线程池的五种状态...

    java并发编程面试题

    java并发编程 基础知识,守护线程与线程, 并行和并发有什么区别? 什么是上下文切换?...ReentrantLock(重入锁)实现原理与公平锁非公平锁区别什么是可重入锁(ReentrantLock)? ThreadLocal内存泄漏分析与

    locks框架:接口.pdf

    可重入性和重入锁: 解释 Lock 接口的可重入性,讲解同一个线程多次获取锁的机制,避免死锁。介绍 ReentrantLock 的实现原理。 Condition 条件变量: 介绍 Lock 接口中的 Condition,它可以实现更复杂的线程等待和...

    读写锁.txt

    ReentrantLock//互斥锁 class CachedData { Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    去故新 Java线程新同步机制

    1、可重入锁ReentrantLock,相当于synchronized块,为临界区提供互斥访问机制。 (1) 相关的接口 创建一个可重入锁 Lock lock = new ReentrantLock(); 请求锁,如果锁被当前另一个线程持有,则阻塞。 void ...

    java多线程安全性基础介绍.pptx

    可重入锁 ReentrantReadWriteLock.ReadLock ReentrantReadWriteLock.WriteLock 隐式锁(内置锁) Synchronized 问题 死锁 性能 线程活跃与线程饥饿 同步工具类 原子操作类 AtomicInteger等 相当于加上...

    个人总结的深入java多线程开发

    7)ReentrantLock可重入的互斥锁定 Lock 32 8)阻塞队列BlockingQueue 34 9)已完成任务队列CompletionService 36 10)计时器CountDownLatch 37 11)周期性同步工具CyclicBarrier 38 12)异步计算的结果Future 40 13)安排...

    基于redis实现分布式锁的原理与方法

    为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchronized进行互斥控制。但是这仅仅对单机环境有效。我们实现分布式锁大概通过三种方式。 redis实现分布式锁 数据库实现...

    java核心知识点整理.pdf

    1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM .........................

    JAVA核心知识点整理(有效)

    1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................

Global site tag (gtag.js) - Google Analytics