java并发-独占锁与共享锁

1 锁的独占与共享

java并发包提供的加锁模式分为独占锁和共享锁,独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。共享锁,则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。AQS的内部类Node定义了两个常量SHARED和EXCLUSIVE,他们分别标识 AQS队列中等待线程的锁获取模式。

很显然,独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。 java的并发包中提供了ReadWriteLock,读-写锁。它允许一个资源可以被多个读操作访问,或者被一个 写操作访问,但两者不能同时进行。

2 锁的公平与非公平

锁的公平与非公平,是指线程请求获取锁的过程中,是否允许插队。在公平锁上,线程将按他们发出请求的顺序来获得锁;而非公平锁则允许在线程发出请求后立即尝试获取锁,如果可用则可直接获取锁,尝试失败才进行排队等待。ReentrantLock提供了两种锁获取方式,FairSyn和NofairSync。结论:ReentrantLock是以独占锁的加锁策略实现的互斥锁,同时它提供了公平和非公平两种锁获取方式。最初看源码时竟然把这两个概念弄混了。

3 AQS提供的模板方法

AQS提供了独占锁和共享锁必须实现的方法,具有独占锁功能的子类,它必须实现tryAcquire、tryRelease、isHeldExclusively等;共享锁功能的子类,必须实现tryAcquireShared和tryReleaseShared等方法,带有Shared后缀的方法都是支持共享锁加锁的语义。Semaphore是一种共享锁,ReentrantLock是一种独占锁。

独占锁获取锁时,设置节点模式为Node.EXCLUSIVE

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

共享锁获取锁,节点模式则为Node.SHARED

     private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        .....
    }

4  对ConditionObject的认识

ReentrantLock是独占锁,而且AQS的ConditionObject只能与ReentrantLock一起使用,它是为了支持条件队列的锁更方便。ConditionObject的signal和await方法都是基于独占锁的,如果线程非锁的独占线程,则会抛出IllegalMonitorStateException。例如signalAll源码:

        public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
        }

我在想,既然Condtion是为了支持Lock的,为什么ConditionObject不作为ReentrantLock的内部类呢?对于实现锁功能的子类,直接扩展它就可以实现对条件队列的支持。但是,对于其它非锁语义的实现类如Semaphore、CountDownLatch等类来说,条件队列是无用的,也会给开发者扩展AQS带来困惑。总之,是各有利弊,大师们的思想,还需要仔细揣摩啊!

时间: 2024-11-06 22:40:09

java并发-独占锁与共享锁的相关文章

java并发 lock锁

Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?这个问题将在下面进行阐述.本文先从synchronized的缺陷讲起,然后再讲述java.util.concurrent.locks包

java 并发(六) --- 锁

      阅读前阅读以下参考资料,文章图片或代码部分来自与参考资料 概览 一张图了解一下java锁. 各种锁 为什么要设置锁的等级 jdk1.6对锁的实现引入了大量的优化,如自旋锁.适应性自旋锁.锁消除.锁粗化.偏向锁.轻量级锁等技术来减少锁操作的开销.锁主要存在四中状态,依次是:无锁状态.偏向锁状态.轻量级锁状态.重量级锁状态,他们会随着竞争的激烈而逐渐升级.注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率. 乐观锁和悲观锁 阅读这篇文章,了解乐观锁和悲观锁 ---- 不可不说

(转)java并发对象锁、类锁、私有锁

转自:http://ifeve.com/java-locks/ 建议参考:http://www.zhihu.com/question/28113814 Java类锁和对象锁实践 感谢[jiehao]同学的投稿,投稿可将文章发送到[email protected] 类锁和对象锁是否会冲突?对象锁和私有锁是否会冲突?通过实例来进行说明. 一.相关约定 为了明确后文的描述,先对本文涉及到的锁的相关定义作如下约定: 1. 类锁:在代码中的方法上加了static和synchronized的锁,或者sync

java并发库--锁

synchronized的缺陷: 被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,获取线程被阻塞时,没有释放锁会导致等待线程无期限的等待下去.另外,多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到.Lock还可以知道线程有没有成功获取到锁. Lock和synchronized的比较 1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性.Lock是一个

深入浅出 Java Concurrency (8): 锁机制 part 3[转]

接上篇,这篇从Lock.lock/unlock开始.特别说明在没有特殊情况下所有程序.API.文档都是基于JDK 6.0的. public void java.util.concurrent.locks.ReentrantLock.lock() 获取锁. 如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1. 如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回. 如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一直处于

深入浅出Java并发包—锁机制(二)

接上文<深入浅出Java并发包—锁机制(一)  >  2.Sync.FairSync.TryAcquire(公平锁) 我们直接来看代码 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (isFirst(current) && compareAndSetStat

【Java并发编程实战】—–“J.U.C”:ReentrantReadWriteLock

ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/读"."读/写"."写/写"操作都不能同时发生.然而在实际的场景中我们就会遇到这种情况:有些资源并发的访问中,它大部分时间都是执行读操作,写操作比较少,但是读操作并不影响数据的一致性,如果在进行读操作时采用独占的锁机制,这样势必会大大降低吞吐量.所以如果能够做

学习笔记:java并发编程学习之初识Concurrent

一.初识Concurrent 第一次看见concurrent的使用是在同事写的一个抽取系统代码里,当时这部分代码没有完成,有许多的问题,另一个同事接手了这部分代码的功能开发,由于他没有多线程开发的经验,所以我就一起帮着分析.最开始看到这个时很烦燥啊,因为自己接触java时间很短,连synchronized都不知道怎么用呢,突然发现有这么个复杂的东西.当时就只好开始学习吧,毕竟是使用嘛,第一目的就是了解清楚这玩意的各个类与方法都干嘛用的,然后看了看同事的代码大概也就清楚了.感觉这和大部分人一样,能

聊聊Java中的锁

乐观锁与悲观锁 乐观锁 乐观锁就是当一个线程A去修改共享数据B时,线程A假设其它线程都不会去修改B,因此线程A在对共享数据B修改时,不会对共享数据B进行加锁,而是线程A在修改时只需要对共享数据B的旧值或数据版本进行校验,如果校验成功,则修改之,如果校验不成功,则修改失败. 具体在我们Java编程中比较常见的有利用数据库的version(版本号)字段的自增操作来实现乐观锁,具体的细节我们这里不作介绍. 这里我们还介绍另外一个在Java中的具体实现,那就是比较并更新原子性操作.比如说AtomicIn