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

简介:

作者:张振华    购买链接:天猫商城  JD商城  当当书店

 

显示锁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并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
1天前
|
Java
浅谈Java的synchronized 锁以及synchronized 的锁升级
浅谈Java的synchronized 锁以及synchronized 的锁升级
5 0
|
2天前
|
设计模式 运维 安全
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第15天】在Java开发中,多线程编程是提升应用程序性能和响应能力的关键手段。然而,它伴随着诸多挑战,尤其是在保证线程安全的同时如何避免性能瓶颈。本文将探讨Java并发编程的核心概念,包括同步机制、锁优化、线程池使用以及并发集合等,旨在为开发者提供实用的线程安全策略和性能优化技巧。通过实例分析和最佳实践的分享,我们的目标是帮助读者构建既高效又可靠的多线程应用。
|
4天前
|
Java 程序员 编译器
Java中的线程同步与锁优化策略
【4月更文挑战第14天】在多线程编程中,线程同步是确保数据一致性和程序正确性的关键。Java提供了多种机制来实现线程同步,其中最常用的是synchronized关键字和Lock接口。本文将深入探讨Java中的线程同步问题,并分析如何通过锁优化策略提高程序性能。我们将首先介绍线程同步的基本概念,然后详细讨论synchronized和Lock的使用及优缺点,最后探讨一些锁优化技巧,如锁粗化、锁消除和读写锁等。
|
4天前
|
Java 编译器
Java并发编程中的锁优化策略
【4月更文挑战第13天】 在Java并发编程中,锁是一种常见的同步机制,用于保证多个线程之间的数据一致性。然而,不当的锁使用可能导致性能下降,甚至死锁。本文将探讨Java并发编程中的锁优化策略,包括锁粗化、锁消除、锁降级等方法,以提高程序的执行效率。
11 4
|
5天前
|
Java 调度 开发者
Java 21时代的标志:虚拟线程带来的并发编程新境界
Java 21时代的标志:虚拟线程带来的并发编程新境界
14 0
|
8天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
8天前
|
安全 Java 开发者
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第9天】本文将深入探讨Java并发编程的核心概念,包括线程安全和性能优化。我们将详细解析Java中的同步机制,包括synchronized关键字、Lock接口以及并发集合等,并探讨它们如何影响程序的性能。此外,我们还将讨论Java内存模型,以及它如何影响并发程序的行为。最后,我们将提供一些实用的并发编程技巧和最佳实践,帮助开发者编写出既线程安全又高效的Java程序。
22 3
|
12天前
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
【4月更文挑战第6天】Java中的`synchronized`关键字用于处理多线程并发,确保共享资源的线程安全。它可以修饰方法或代码块,实现互斥访问。当用于方法时,锁定对象实例或类对象;用于代码块时,锁定指定对象。过度使用可能导致性能问题,应注意避免锁持有时间过长、死锁,并考虑使用`java.util.concurrent`包中的高级工具。正确理解和使用`synchronized`是编写线程安全程序的关键。
|
10天前
|
Java
Java 并发编程:深入理解线程池
【4月更文挑战第8天】本文将深入探讨 Java 中的线程池技术,包括其工作原理、优势以及如何使用。线程池是 Java 并发编程的重要工具,它可以有效地管理和控制线程的执行,提高系统性能。通过本文的学习,读者将对线程池有更深入的理解,并能在实际开发中灵活运用。