`
春花秋月何时了
  • 浏览: 41806 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Java并发包核心框架Lock接口

 
阅读更多

引言

终于走到这一步,在JDK5之前,Java都是通过synchronized关键字实现同步锁功能的,通过前面对synchronized关键字的阐述,相信我们已经非常了解了其特性,它是为了保证在多线程并发下对共享资源的访问的线程安全。从JDK5开始,Java提供了另一种加锁的方式即我们在可见性、有序性与原子性以及非原子协定章节中提到过的Lock显示锁。Lock显示锁也是Java并发包存在的主要原因,它提供了与synchronized关键字类似的线程同步功能,并且使用Lock显示锁也满足happens-before原则。Lock显示锁和synchronized关键字支撑起整个Java语言层面的线程同步解决办法,两者各有优劣,适用于不同的场景。

 

Lock接口

Lock作为Java并发包的基础核心组件,但是其只是一个接口,因为以模板方式实现的AbstractQueuedSynchronizer才是显示锁Lock最底层最精细的实现,但是Lock锁作为一个使用于不同场景的上层同步器,其加锁的特性在不同的场景肯定有不同的需求,所以为了达到不同场景的加锁特性,就需要以AQS的底层实现为基石,通过实现其特定的模板方法来达到不同的特性需要(例如:重入性,公平性,读写锁等)。说了半天这不就是我们在Java并发包核心框架AQS之一同步阻塞与唤醒续章节中对“自定义AQS同步器”的做法吗。

没错,自定义同步器就是在构造满足不同场景的Lock显示锁,还记得在“自定义同步器”时,我们首先要做的第一件事情吗,那就是需要先定义一个外部接口,利用该接口屏蔽Lock锁的具体实现,只对外提供几个简单的对Lock锁的操作方法。既然每次自定义同步器时都需要先定义一个外部接口,而Java并发包作为JDK又需要为开发者提供几种最常见最通用的显示锁的实现,因此一个通用的显示锁接口自然就诞生了,在实现自定义的同步器的时候,我们直接都实现这个接口就可以了,这个通用的接口就是我们这里说的Lock接口。

 

下面我们先看看Lock接口对外提供的需要开发者实现的接口方法:


 从Lock接口的方法可以看出,除了阻塞式获取锁的方法void lock()与 synchronized关键字的语言几乎完全一样之外,另外几个获取锁的方法都是synchronized关键字所不能提供的,这就是Lock显示锁比synchronized关键字更强大的地方。 

 

Lock接口的使用

 Lock lock = new ReentrantLock(); //以ReentrantLock为例
	lock.lock();//不能将锁的获取操作放在try块里面,因为如果在获取锁时发生了异常也会导致锁的无故释放
	try {
		// 可能会出现线程安全的操作
	} finally {
		// 一定在finally中释放锁
		// 也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
		lock.unlock();
	}

   Lock显示锁使用时,最需要注意的有两点,1,不要将获取锁的操作放进try块中;2,一定要记住在finally语句块中执行unlock()释放锁,否则可能导致死锁。

 

Lock显示锁与synchronize隐式锁的区别

实现方式区别

synchronize是由C/C++书写的平台相关的native代码实现,底层使用CAS对锁对象的对象头中的Mark Word进行操作以标识加/解锁,至于锁的更多细节信息则被包含在Mark Word中指针指向的对象(Lock Record, 线程ID 或者ObjectMonitor)。在实现重量级锁的时候,JVM实现的更精细,把等待队列分为ContentionList和EntryList,目的是为了降低线程的出列速度。另外使用了一个_WaitSet队列存储条件等待的线程。

Lock显示锁是由Java编写的AbstractQueuedSynchronizer队列同步器实现的,其内部使用CAS对一个volatile修饰的state变量进行操作以标识加/解锁,使用了一个双向链表存储同步等待的阻塞线程,而对于不同的条件等待阻塞线程,同样采用不同的双向链表存放。

就实现上来看,Lock显示锁未必会比synchronize好,由于synchronize是直接使用native代码实现的,毕竟JAVA语言效率和native语言效率比大多数情况总有不如,并且synchronize实现了自旋锁,并针对不同的系统和硬件体系进行了锁优化。

运行机制的区别:

synchronize是独占式的悲观锁,为什么都说synchronize是悲观锁呢?应该这主要是指重量级锁的情况,因为在进入synchronize同步块之前,重量级锁都悲观的认为数据已经被改变,必须要先获取到相应的锁,不论这时候有没有出现多线程竞争的情况。

而Lock锁在实现加锁机制的时候,使用的是CAS,并假设不存在竞争的情况,也就是假设同步资源state变量没有被修改,只有在CAS操作失败之后才会进行加锁,而在读写锁的实现中,甚至进行了更细粒度的加锁条件判断。

功能区别:

synchronize隐式锁,完全由JVM调度独占锁,所以功能上更加单一。对于线程通信的实现也比较简单,只能使用一个条件判断的wait()/notify()机制。

Lock显示锁则提供了非阻塞式、响应中断式的加锁实现,而且还能根据需要实现适用于不同场景的读写锁、共享锁、公共锁、非公平锁等,功能更加强大,使用上更加灵活。面对复杂的业务逻辑更加得心应手。在线程通信的实现方面也更加强大,可以使用绑定多个Condition实例获得不同的条件等待。比wait()/notify()更灵活。

使用方式的不同:

使用synchronize更加简单,获取和释放锁的过程都由JVM自己完成,开发人员使用更加容易放心。

使用Lock显示锁,开发人员必须手动获取锁,并且不能忘记在finally语句块中释放锁,否则会产生死锁。

日志分析的差异:

据说使用synchronize在出现死锁等需要分析线程dump数据查找问题的时候,更加清晰方面,而使用Lock显示锁则没有那么方便,不过我没有亲自验证过,但说话LockSupport中不是存在可以设置线程是被谁阻塞的方法setBlocker吗?

 

Lock显示锁的使用场景

并不是说在Lock锁出现之后,我们就应该毫无缘由的替换掉所有使用synchronize的加锁方式,只有在出现如下几种情况的时候我们才应当考虑使用Lock显示,否则还是应该使用synchronize关键字,毕竟它更加简单,而且优化的也很好,其性能甚至将来可能还会更好。

1.对Lock 的某个高级特性有明确的需要,例如响应超时或中断,或非阻塞式甚至需要实现特定的同步器,或者wait/notify方式的线程通信不方便(例如多条件判断)。

2.有明确的证据(而不是仅仅是怀疑)表明在特定情况下,同步已经成为可伸缩性的瓶颈。

3.在多线程高度竞争条件下可以选择使用Lock显示锁的实现ReentrantLock ,因为它在高争用的时候性能更好。

 

为什么在使用Lock显示锁的时候要持保守态度呢,因为对于Lock显示锁来说,synchronized 仍然有一些优势。比如,在使用 synchronized 的时候,不可能忘记释放锁;在退出 synchronized 块时,JVM 会为您做这件事。您很容易忘记用 finally 块释放锁,这对程序非常有害。另一方面几乎每个开发人员都熟悉 synchronized,但不是每个开发人员都属性Lock显示锁的使用。这样更方便代码维护。

 

总结起来Lock 框架是同步的兼容替代品,它提供了 synchronized 没有提供的许多特性,它的实现在高争用下提供了更好的性能。 但是还不足以完全替代synchronized ,我们应当根据我们的需要作出是否要使用Lock显示锁的判断,大多数情况下,我们还是优先考虑使用synchronized ,只有在真正需要 Lock 的时候才用它。

 

Lock显示锁的通用实现

在JDK8 中,Java通过Lock接口主要实现了如下这些显示锁:ReentrantLock、ReadLock、ReadLockView、WriteLock、WriteLockView。其中的ReentrantLock就是synchronized 锁的对应Java版本实现,而ReadLock和WriteLock则是实现ReentrantReadWriteLock读写锁的内部基础,ReadLockView和WriteLockView则是实现StampedLock锁的内部基础,ReentrantLock甚至也是实现ConcurrentHashMap内部锁Segment的继承父类。在接下来的章节我们将会逐个对这些Lock接口的实现类,这些显示锁的特性进行了解分析。

 

  • 大小: 23 KB
分享到:
评论

相关推荐

    juc详解juc详解juc详解juc详解juc详解juc详解juc详解

    - `ExecutorService`是线程池的核心接口,它负责管理线程的创建和执行。通过`Executors`类提供的静态工厂方法,可以创建不同类型的线程池,如`FixedThreadPool`、`SingleThreadExecutor`、`CachedThreadPool`和`...

    java concurrent 包 详细解析

    Java并发包(java.concurrent)是Java平台中处理多线程编程的核心工具包,它提供了丰富的类和接口,使得开发者能够高效、安全地编写多线程程序。这个包的设计目标是提高并发性能,减少同步代码的复杂性,并提供高级...

    javaStrengthen:Java 高级特性增强。包括多线程增强、Java并发包、Java JMS技术、Java JVM技术、Java动态代理以及反射

    2. **Java并发包**: `java.util.concurrent`(JUC)包是Java并发编程的核心,包含各种并发工具类,如`Semaphore`(信号量)、`CountDownLatch`(计数器)、`CyclicBarrier`(回环栅栏)、`ThreadPoolExecutor`...

    多线程编程的核心思想.doc

    Lock 接口是 Java 并发包中的一个核心接口,提供了同步机制,用于多线程环境下的线程安全。Lock 接口中的方法有 lock()、lockInterruptibly()、tryLock()、tryLock(long time, TimeUnit unit)和 unlock()。 * lock...

    java-core:JAVA集合包,线程池,并发包,NIO等API及链接相关学习

    这个名为"java-core"的资源集重点关注了几个关键领域:集合框架、多线程、线程池、并发包以及非阻塞I/O(NIO)。让我们逐一深入探讨这些主题。 1. **Java集合框架**: Java集合框架是处理对象集合的一组接口和类,...

    Java多线程程序设计

    Java并发包(java.util.concurrent)提供了一系列线程安全的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等,它们在多线程环境下仍能保持数据一致性。 七、Future和Callable接口 Callable接口类似于Runnable...

    黑马程序员-java多线程技术01

    7. **并发集合**:Java并发包(java.util.concurrent)提供了许多线程安全的集合,如ConcurrentHashMap, CopyOnWriteArrayList等,这些集合在多线程环境下性能较好。 8. **线程池**:Executor框架允许我们创建和...

    java思维导图.rar

    6. **多线程**:Java提供了丰富的多线程支持,包括Thread类、Runnable接口和并发包(java.util.concurrent)。了解线程的生命周期、同步机制(synchronized关键字、Lock接口)以及并发工具类,有助于编写高效、安全...

    80w字Java面试宝典(非常全)

    ava八股文通常是指Java技术面试中常见的知识点集合,...线程池及并发包:Executors、ForkJoinPool、CountDownLatch、CyclicBarrier等。 并发优化:CAS、无锁编程、AQS原理等。 内容不限于这些,适合中、大厂面试来用!

    java线程文档大全

    9. **线程池**:ExecutorService和ThreadPoolExecutor是Java并发框架中的核心组件,用于管理和调度线程,提高性能并避免资源过度消耗。线程池可以根据需求配置核心线程数、最大线程数、任务队列等参数。 10. **信号...

    java经典代码

    Java并发包(java.util.concurrent)提供了许多高效、安全的并发工具,如ExecutorService、Semaphore和ConcurrentHashMap等。 此外,文件处理、网络编程、XML/JSON解析也是Java开发中的常见任务。例如,使用...

    java并发编程实践 pdf

    Java并发包中的Executor框架是处理线程的核心,它包括ExecutorService、Executors、ThreadPoolExecutor等类,可以帮助我们更好地管理线程池。 - **ExecutorService**:用于执行任务的服务接口。 - **Executors**:...

    java 面试

    或者深入理解并发包中的synchronized、volatile关键字,以及Lock接口的实现机制。此外,对于一些热门框架如Spring、MyBatis的源码阅读和分析能力也是常考点,了解其设计理念和内部工作原理,可以帮助你更好地应对...

    [Java并发编程实践].(Java.Concurrency.in.Practice).Brian.Goetz.文字版(1)

    3. **并发集合**:Java并发包(java.util.concurrent)提供了线程安全的集合类,如ConcurrentHashMap、CopyOnWriteArrayList和BlockingQueue等。这些集合在内部实现了高效并发控制,允许在并发环境下安全地操作。 4...

    学习java必看

    11. **Java标准库**:深入学习Java的标准库,如util包中的各种工具类,lang包中的基础类,以及并发包(concurrent)中的并发工具。 12. **框架与库**:学习流行的Java框架如Spring、Hibernate、MyBatis等,以及如何...

    JAVA API 文档中文版chm

    10. **并发工具**:Java并发包(java.util.concurrent)提供了许多高级并发工具,如ExecutorService、Semaphore、CountDownLatch等,用于构建高性能并发程序。 11. **JavaBeans和事件模型**:JavaBeans规范定义了一...

    java培训教程-清华版(共5部分)-3

    5. **并发工具类**:Java并发包(java.util.concurrent)提供了一系列高级并发工具,如Semaphore(信号量)、CountDownLatch(计数器门锁)和CyclicBarrier(循环屏障)等,用于更灵活的线程协作。 通过本教程的...

    阿里Java并发程序设计教程

    ConcurrentMap的putIfAbsent方法和CopyOnWriteArrayList类是Java并发包中的Lock-free结构,它们通过避免锁的使用来提高并发性能。 知识点六:锁的使用经验。锁是同步并发操作的关键机制之一,Java提供了多种锁机制...

    Java并发编程

    9. **并发工具类**:Java并发包(`java.util.concurrent`)提供了丰富的工具类,如`Atomic*`类(原子操作)、`CyclicBarrier`(回旋栅栏)、`Phaser`(屏障)等,这些工具可以帮助我们编写高效且线程安全的代码。...

    《java并发编程实践》

    4. **并发容器**:Java并发包(java.util.concurrent)提供了一系列优化的并发容器,如ConcurrentHashMap、BlockingQueue、CopyOnWriteArrayList等,这些容器设计巧妙,能够在多线程环境下保持高效性能。 5. **原子...

Global site tag (gtag.js) - Google Analytics