《Java并发编程从入门到精通》显示锁Lock和ReentrantLock

简介:

显示锁Lock和ReentrantLock

Lock是一个接口提供了无条件的、可轮询的、定时的、可中断的锁获取操作,所有加锁和解锁的方法都是显式的。包路径是:java.util.concurrent.locks.Lock。核心方法是lock(),unlock(),tryLock(),实现类有ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock。

看一下Lock接口有如下方法:

public abstract interface Lock

{

public abstract void lock();

public abstract void lockInterruptibly() throws InterruptedException;

public abstract boolean tryLock();

public abstract boolean tryLock(long paramLong , TimeUnit paramTimeUnit) throws InterruptedException;

public abstract void unlock();

public abstract Condition newCondition();

}

对应的解说如下:

void lock();获取锁。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。
void lockInterruptibly() throws InterruptedException;如果当前线程未被中断,则获取锁。如果锁可用,则获取锁,并立即返回。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在发生以下两种情况之一以前,该线程将一直处于休眠状态:锁由当前线程获得;或者其他某个线程中断 当前线程,并且支持对锁获取的中断。如果当前线程:在进入此方法时已经设置了该线程的中断状态;或者在获取锁时被中断 ,并且支持对锁获取的中断,则将抛出  InterruptedException ,并清除当前线程的已中断状态。
boolean tryLock();仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值  true 。如果锁不可用,则此方法将立即返回值  false 。通常对于那些不是必须获取锁的操作可能有用。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。如果锁可用,则此方法将立即返回值  true 。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在发生以下三种情况之一前,该线程将一直处于休眠状态:
void unlock();释放锁。对应于lock()、tryLock()、tryLock(xx)、lockInterruptibly()等操作,如果成功的话应该对应着一个unlock(),这样可以避免死锁或者资源浪费。

newCondition() 返回用来与此 Lock 实例一起使用的 Condition 实例。

ReentrantLock是Lock的实现类,是一个互斥的同步器,它具有扩展的能力。在竞争条件下,ReentrantLock 的实现要比现在的 synchronized 实现更具有可伸缩性。(有可能在 JVM 的将来版本中改进 synchronized 的竞争性能)这意味着当许多线程都竞争相同锁定时,使用 ReentrantLock 的吞吐量通常要比 synchronized 好。换句话说,当许多线程试图访问 ReentrantLock 保护的共享资源时,JVM 将花费较少的时间来调度线程,而用更多个时间执行线程。虽然 ReentrantLock 类有许多优点,但是与同步相比,它有一个主要缺点 — 它可能忘记释放锁定。ReentrantLock实在工作中对方法块加锁使用频率最高的。

使用方法如下:

class X {

private final ReentrantLock lock = new ReentrantLock();

// …

public void m() {

lock.lock(); // 获得锁

try {

// … 方法体

finally {

lock.unlock();//解锁

}

}

}
Lock与synchronized 的比较:

1:Lock使用起来比较灵活,但是必须有释放锁的动作;

2:Lock必须手动释放和开启锁,synchronized 不需要;

3:Lock只适用与代码块锁,而synchronized 对象之间的互斥关系;

请注意以下两种方式的区别:

第一种方式:两个方法之间的锁是独立的。如下:

public class ReentrantLockDemo {

public static void main(String[] args) {

final Count ct = new Count();

for (int i = 0; i < 2; i++) {

new Thread() {

@Override

public void run() {

ct.get();

}

}.start();

}

 

for (int i = 0; i < 2; i++) {

new Thread() {

@Override

public void run() {

ct.put();

}

}.start();

}

}

}

class Count {

public void get() {

final ReentrantLock lock = new ReentrantLock();

try {

lock.lock(); // 加锁

System. out.println(Thread.currentThread().getName() + “get begin”);

Thread. sleep(1000L);// 模仿干活

System. out.println(Thread.currentThread().getName() + “get end”);

lock.unlock(); // 解锁

catch (InterruptedException e) {

e.printStackTrace();

}

}

public void put() {

final ReentrantLock lock = new ReentrantLock();

try {

lock.lock(); // 加锁

System. out.println(Thread.currentThread().getName() + “put begin”);

Thread. sleep(1000L);// 模仿干活

System. out.println(Thread.currentThread().getName() + “put end”);

lock.unlock(); // 解锁

catch (InterruptedException e) {

e.printStackTrace();

}

}

}

运行结果如下(每次运行结果都是不一样的,仔细体会一下):

Thread-0get begin

Thread-1get begin

Thread-2put begin

Thread-3put begin

Thread-0get end

Thread-2put end

Thread-3put end

Thread-1get end

第二种方式,两个方法之间使用相同的锁。

ReentrantLockDemo 类的内容不变,将Count中的ReentrantLock改成全局变量,如下所示:

class Count {

final ReentrantLock lock = new ReentrantLock();

public void get() {

try {

lock.lock(); // 加锁

System. out.println(Thread.currentThread().getName() + “get begin”);

Thread. sleep(1000L);// 模仿干活

System. out.println(Thread.currentThread().getName() + “get end”);

lock.unlock(); // 解锁

catch (InterruptedException e) {

e.printStackTrace();

}

}

public void put() {

try {

lock.lock(); // 加锁

System. out.println(Thread.currentThread().getName() + “put begin”);

Thread. sleep(1000L);// 模仿干活

System. out.println(Thread.currentThread().getName() + “put end”);

lock.unlock(); // 解锁

catch (InterruptedException e) {

e.printStackTrace();

}

}

}

运行结果如下(每次运行结果一样的,仔细体会一下):

Thread-0get begin

Thread-0get end

Thread-1get begin

Thread-1get end

Thread-2put begin

Thread-2put end

Thread-3put begin

Thread-3put end

相关文章
|
6月前
|
Java
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
因为本文说的是ReentrantLock源码,因此会默认,大家对AQS有基本的了解(比如同步队列、条件队列大概> 长啥样?)。 不懂AQS的小朋友们,你们好呀!也欢迎先看看这篇
127 13
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
|
6月前
|
Java
【源码】【Java并发】【AQS】从ReentrantLock、Semaphore、CutDownLunch、CyclicBarrier看AQS源码
前言 主播觉得,AQS的原理,就是通过这2个队列的协助,实现核心功能,同步队列(CLH队列)和条件队列(Condition队列)。 同步队列(CLH队列) 作用:管理需要获...
119 18
【源码】【Java并发】【AQS】从ReentrantLock、Semaphore、CutDownLunch、CyclicBarrier看AQS源码
|
6月前
|
监控 Java API
【Java并发】【ReentrantLock】适合初学体质的ReentrantLock入门
前言 什么是ReentrantLock? ReentrantLock 是 Java 并发包 (java.util.concurrent.locks) 中的一个类,它实现了 Lock 接口,提供了与
235 10
【Java并发】【ReentrantLock】适合初学体质的ReentrantLock入门
|
10月前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
128 0
|
7月前
|
存储 架构师 安全
深入理解Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(图解+史上最全)
锁状态bits1bit是否是偏向锁2bit锁标志位无锁状态对象的hashCode001偏向锁线程ID101轻量级锁指向栈中锁记录的指针000重量级锁指向互斥量的指针010尼恩提示,讲完 如减少锁粒度、锁粗化、关闭偏向锁(-XX:-UseBiasedLocking)等优化手段 , 可以得到 120分了。如减少锁粒度、锁粗化、关闭偏向锁(-XX:-UseBiasedLocking)等‌。JVM锁的膨胀、锁的内存结构变化相关的面试题,是非常常见的面试题。也是核心面试题。
深入理解Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(图解+史上最全)
|
11月前
|
缓存 Java
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
本文介绍了几种常见的锁机制,包括公平锁与非公平锁、可重入锁与不可重入锁、自旋锁以及读写锁和互斥锁。公平锁按申请顺序分配锁,而非公平锁允许插队。可重入锁允许线程多次获取同一锁,避免死锁。自旋锁通过循环尝试获取锁,减少上下文切换开销。读写锁区分读锁和写锁,提高并发性能。文章还提供了相关代码示例,帮助理解这些锁的实现和使用场景。
282 4
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
|
11月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
776 6
|
11月前
|
设计模式 安全 Java
Java 多线程并发编程
Java多线程并发编程是指在Java程序中使用多个线程同时执行,以提高程序的运行效率和响应速度。通过合理管理和调度线程,可以充分利用多核处理器资源,实现高效的任务处理。本内容将介绍Java多线程的基础概念、实现方式及常见问题解决方法。
364 1
|
11月前
|
Java 开发者
Java 中的锁是什么意思,有哪些分类?
在Java多线程编程中,锁用于控制多个线程对共享资源的访问,确保数据一致性和正确性。本文探讨锁的概念、作用及分类,包括乐观锁与悲观锁、自旋锁与适应性自旋锁、公平锁与非公平锁、可重入锁和读写锁,同时提供使用锁时的注意事项,帮助开发者提高程序性能和稳定性。
454 3
|
11月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
124 4