《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 

时间: 2024-11-03 16:21:52

《Java并发编程从入门到精通》显示锁Lock和ReentrantLock的相关文章

《 Java并发编程从入门到精通》 Java线程池的监控

本文是< Java并发编程从入门到精通>第9章 线程的监控及其日常工作中如何分析的9.1节 Java线程池的监控.   看不到不等于不存在!让我们来看看工作中是如何找问题解决问题的. 鸟欲高飞先振翅,人求上进先读书. 京东,亚马逊,当当均有销售. 9.1 Java线程池的监控 如果想实现线程池的监控,必须要自定义线程池继承ThreadPoolExecutor类,并且实现beforeExecute,afterExecute和terminated方法,我们可以在任务执行前,执行后和线程池关闭前干一

《 Java并发编程从入门到精通》目录和序言

目 录 第一部分:线程并发基础   第1章 概念部分   1 1.1 CPU核心数.线程数 (主流cpu,线程数的大体情况说一下) 1 1.2 CPU时间片轮转机制 2 1.3 什么是进程和什么是线程 4 1.4 进程和线程的比较 5 1.5 什么是并行运行 7 1.6 什么是多并发运行 8 1.7 什么是吞吐量 9 1.8  多并发编程的意义及其好处和注意事项 10 1.9  分布式与并发运算关系 11 1.10 Linux和Window多并发可以采取不的一样机制(apache和tomcat?

《 Java并发编程从入门到精通》第5章 多线程之间交互:线程阀

作者:张振华    购买链接:天猫商城 (投入多少,收获多少.参与多深,领悟多深,京东,亚马逊,当当均有销售.)   5.1 线程安全的阻塞队列BlockingQueue (1)先理解一下Queue.Deque.BlockingQueue的概念: Queue(队列) :用于保存一组元素,不过在存取元素的时候必须遵循先进先出原则.队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有

《 Java并发编程从入门到精通》 常见的内存溢出的三种情况

作者:张振华    购买链接:天猫商城  JD商城  当当书店 鸟欲高飞先振翅,人求上进先读书.本文是原书的第9章 线程的监控及其日常工作中如何分析里的9.3.3节常见的内存溢出的三种情况. 3. 常见的内存溢出的三种情况: 1)JVM Heap(堆)溢出:java.lang.OutOfMemoryError: Java heap space JVM在启动的时候会自动设置JVM Heap的值, 可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置.Heap的大小是Young Gene

《 Java并发编程从入门到精通》Thread安全与不安全

作者:张振华    购买链接:天猫商城  JD商城  当当书店 鸟欲高飞先振翅,人求上进先读书.本文是原书的第3章  Thread安全3.2 什么是不线程安全.3.3什么是线程不安全. 3.2 什么是不安全? 当多个线程同时操作一个数据结构的时候产生了相互修改和串行的情况,没有保证数据的一致性,我们通常称之这种设计的代码为"线程不安全的". 有这么一个场景,假设5个用户,都来给一个数字加1的工作,那么最后应该是得到加5的结果:看一下下面的事例: 单个用户干活类:Count : publ

Java网络编程从入门到精通

Hibernate从入门到精通(十一)多对多双向关联映射 Hibernate从入门到精通(十)多对多单向关联映射 Hibernate从入门到精通(九)一对多双向关联映射 Hibernate从入门到精通(八)一对多单向关联映射 Hibernate从入门到精通(七)多对一单向关联映射 Hibernate从入门到精通(六)一对一双向关联映射 Hibernate从入门到精通(五)一对一单向关联映射 Hibernate从入门到精通(四)基本映射 Hibernate从入门到精通(三)Hibernate配置文

Java网络编程从入门到精通(34)

Java网络编程从入门到精通(34):读写缓冲区中的数据---使用get和put方法按顺序读写单个数据 对于缓冲区来说,最重要的操作就是读写操作.缓冲区提供了两种方法来读写缓冲区中的数据:get.put方法和array方法.而get.put方法可以有三种读写数据的方式:按顺序读写单个数据.在指定位置读写单个数据和读写数据块.除了上述的几种读写数据的方法外,CharBuffer类还提供了用于专门写字符串的put和append方法.在本文及后面的文章中将分别介绍这些读写缓冲区的方法. 虽然使用all

Java网络编程从入门到精通 (9):使用isXxx方法判断地址类型

本文为原创,如需转载,请注明作者和出处,谢谢! 上一篇:Java网络编程从入门到精通(8):用getAddress方法获得IP地址     IP地址分为普通地址和特殊地址.在前面的文章中所使用的大多数都是普通的IP地址,在本文中将介绍如何利用InetAddress类提供的十个方法来确定一个IP地址是否是一个特殊的IP地址. 一.isAnyLocalAddress方法     当IP地址是通配符地址时返回true,否则返回false.这个通配符地址对于拥有多个网络接口(如两块网卡)的计算机非常拥有

Java网络编程从入门到精通(34):读写缓冲区中的数据---使用get和put方法按顺序读写单个数据

本文为原创,如需转载,请注明作者和出处,谢谢! 上一篇:Java网络编程从入门到精通(33):非阻塞I/O的缓冲区(Buffer)     对于缓冲区来说,最重要的操作就是读写操作.缓冲区提供了两种方法来读写缓冲区中的数据:get.put方法和array方法.而get.put方法可以有三种读写数据的方式:按顺序读写单个数据.在指定位置读写单个数据和读写数据块.除了上述的几种读写数据的方法外,CharBuffer类还提供了用于专门写字符串的put和append方法.在本文及后面的文章中将分别介绍这