上面我们用儿子和父亲做了一个例子,儿子太猖狂了,花钱根本没有谱。
下面我们再做一个例子,讲解ReadWriteLock的使用,例子和刚才差不多,但是又多了一个角色母亲,而且父母只负责监督,不再存钱。
儿子每三秒都会去花钱一次,而父母每秒都在监督这个卡的使用情况。
所有 ReadWriteLock 实现都必须保证 writeLock 操作的内存同步效果也要保持与相关 readLock 的联系。也就是说,成功获取读锁的线程会看到写入锁之前版本所做的所有更新。
与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。
在 writer 释放写入锁时,reader 和 writer 都处于等待状态,在这时要确定是授予读取锁还是授予写入锁。Writer 优先比较普遍,因为预期写入所需的时间较短并且不那么频繁。Reader 优先不太普遍,因为如果 reader 正如预期的那样频繁和持久,那么它将导致对于写入操作来说较长的时延。公平或者“按次序”实现也是有可能的。
在 reader 处于活动状态而 writer 处于等待状态时,确定是否向请求读取锁的 reader 授予读取锁。Reader 优先会无限期地延迟 writer,而 writer 优先会减少可能的并发。
我们创建信用卡类:
package com.entity; public class BankCard { private String cardid = "XZ456789"; private int balance = 10000; public String getCardid() { return cardid; } public void setCardid(String cardid) { this.cardid = cardid; } public int getBalance() { return balance; } public void setBalance(int balance) { this.balance = balance; } }
里面有卡号和父母已经存的钱。
儿子花钱首先要获得写的锁把卡锁了,然后再花钱。之后放开这个锁。
package com.thread; import java.util.concurrent.locks.ReadWriteLock; import com.entity.BankCard; /** * @说明 儿子类,只消费 */ public class Consumer implements Runnable { BankCard bc = null; ReadWriteLock lock = null; Consumer(BankCard bc, ReadWriteLock lock) { this.bc = bc; this.lock = lock; } public void run() { try { while(true){ lock.writeLock().lock(); System.out.print("儿子要消费,现在余额:" + bc.getBalance() + "\t"); bc.setBalance(bc.getBalance() - 2000); System.out.println("儿子消费2000元,现在余额:" + bc.getBalance()); lock.writeLock().unlock(); Thread.sleep(3 * 1000); } } catch (Exception e) { e.printStackTrace(); } } }
父母类只监督这个卡的使用,获得的是读的锁。
package com.thread; import java.util.concurrent.locks.ReadWriteLock; import com.entity.BankCard; /** * @说明 父母类,只监督 */ public class Consumer2 implements Runnable { BankCard bc = null; int type = 0; ReadWriteLock lock = null; Consumer2(BankCard bc, ReadWriteLock lock,int type) { this.bc = bc; this.lock = lock; this.type = type; } public void run() { try { while(true){ lock.readLock().lock(); if(type==2) System.out.println("父亲要查询,现在余额:" + bc.getBalance()); else System.out.println("老妈要查询,现在余额:" + bc.getBalance()); //lock.readLock().unlock(); Thread.sleep(1 * 1000); } } catch (Exception e) { e.printStackTrace(); } } }
运行程序,儿子开始花钱,父母两人一直在查看花钱情况。
package com.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import com.entity.BankCard; public class MainThread { public static void main(String[] args) { BankCard bc = new BankCard(); ReadWriteLock lock = new ReentrantReadWriteLock(); ExecutorService pool = Executors.newCachedThreadPool(); Consumer cm1 = new Consumer(bc, lock); Consumer2 cm2 = new Consumer2(bc, lock , 1); Consumer2 cm3 = new Consumer2(bc, lock , 2); pool.execute(cm1); pool.execute(cm2); pool.execute(cm3); } }
我们来看一下运行结果:
儿子要消费,现在余额:10000 儿子消费2000元,现在余额:8000 老妈要查询,现在余额:8000 父亲要查询,现在余额:8000 父亲要查询,现在余额:8000 老妈要查询,现在余额:8000 老妈要查询,现在余额:8000 父亲要查询,现在余额:8000 儿子要消费,现在余额:8000 儿子消费2000元,现在余额:6000 父亲要查询,现在余额:6000 老妈要查询,现在余额:6000 老妈要查询,现在余额:6000 父亲要查询,现在余额:6000 父亲要查询,现在余额:6000 老妈要查询,现在余额:6000 老妈要查询,现在余额:6000 儿子要消费,现在余额:6000 儿子消费2000元,现在余额:4000 父亲要查询,现在余额:4000
读写锁是互斥的,但是对于读来说没有互斥性。
也就是说读和写必须分开,但是资源可以同时被几个线程访问。不管是读还是写没有释放锁,其他线程就一直等待锁的释放。
我们来注释父母监督时锁的释放:
lock.readLock().unlock();
儿子要消费,现在余额:10000 儿子消费2000元,现在余额:8000 父亲要查询,现在余额:8000 老妈要查询,现在余额:8000 老妈要查询,现在余额:8000 父亲要查询,现在余额:8000 老妈要查询,现在余额:8000 父亲要查询,现在余额:8000 老妈要查询,现在余额:8000 父亲要查询,现在余额:8000
可以看到儿子花了一次钱后,父母把卡给锁了,儿子不能在花钱,但是父母两个人都可以一直查询卡的余额。
请您到ITEYE看我的原创:http://cuisuqiang.iteye.com
或支持我的个人博客,地址:http://www.javacui.com
相关推荐
在Java中,`java.util.concurrent.locks.ReadWriteLock`接口提供了这种功能,它有两个主要的方法:`readLock()` 和 `writeLock()`. 这两个方法分别返回一个读锁和写锁,它们可以被不同的线程独立获取和释放,从而...
ReadWriteLock 是Java并发编程中一个强大的工具,它通过分离读锁和写锁,允许多个线程并发地读取共享数据,同时保证了写操作的互斥性。这种机制特别适合于读多写少的场景,可以显著提高程序的性能和吞吐量。在使用 ...
在 Java 中,锁有多种不同的实现机制,如可重入锁、读写锁、互斥锁、悲观锁、乐观锁、公平锁、锁消除、独享锁、共享锁等。 可重入锁 可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然...
Java 读写锁是Java并发编程中的一种重要机制,它为多线程环境下的数据访问提供了更为精细的控制。在Java的`java.util....通过阅读和分析`readwritelock`这个示例,开发者可以更好地掌握如何在Java中使用读写锁。
1. **内置锁(Synchronized)**:这是Java中最基本的锁机制,通过关键字`synchronized`实现。它提供了对方法或代码块的互斥访问,确保同一时刻只有一个线程能够执行特定代码。内置锁在退出同步块或方法时自动释放,...
《Java并发编程学习宝典(漫画版)》中提到的23章主要讲解了Java并发编程中的一个重要概念——ReadWriteLock,即读写锁。ReadWriteLock是`java.util.concurrent.locks`包下的一个接口,它是对传统互斥锁的一个扩展,...
### Java并发编程实践-电子书-03章知识点解析 #### 3.1 java.util....通过以上章节的详细讲解,读者可以深入理解Java并发编程的核心概念和技术,掌握在多线程环境中编写高性能、高可靠性的应用程序所需的关键技能。
本文将详细介绍Java中几种常见的锁类型及其之间的区别。 #### 一、公平锁与非公平锁 **1. 公平锁** 公平锁遵循先进先出的原则,即线程获取锁的顺序与其请求锁的顺序相同。这种锁可以确保没有线程被饿死,但同时也...
在Java中,线程是并发的基础,它代表了程序中的独立执行流。Java提供了多种方式来创建和管理线程,包括继承Thread类和实现Runnable接口。通过线程池(如ExecutorService)可以有效地管理和调度大量线程,避免频繁...
在Java中,多线程允许程序同时执行多个任务,提高CPU的利用率,而锁则是用来控制多线程间共享资源的访问,确保数据的一致性和完整性。 一、Java多线程 1. **线程创建方式** - 继承Thread类:自定义类继承Thread类...
本文主要探讨了Java中的两种广义锁概念——乐观锁和悲观锁,以及自旋锁和适应性自旋锁的区别和应用场景。 1. 乐观锁与悲观锁: 乐观锁认为在读取数据时不会有其他线程修改,因此在读取时不加锁,而在更新数据时检查...
Java中的锁机制主要用于解决多线程环境下的资源竞争问题。在并发编程中,为了保证数据一致性与正确性,通常需要采用各种锁来控制对共享资源的访问。Java语言提供了多种锁机制,包括`synchronized`关键字、`...
在Java中,内置锁是通过synchronized关键字实现的,而显示锁则是通过java.util.concurrent.locks包中的接口和类实现的。显示锁的一个重要接口是Lock,它提供了对锁操作的更细粒度控制,而ReentrantLock是Lock的一个...
- **内置锁(Synchronized)**:Java中的内置锁是通过关键字`synchronized`实现的,它提供了对方法或代码块的互斥访问,确保同一时间只有一个线程执行。 - **显式锁(Lock)**:Java并发包`java.util.concurrent....
Java 中的锁 - **定义**:锁是一种同步机制,用于控制多线程间的访问权限。 - **常见锁类型**: - **悲观锁**:假设会发生冲突,每次访问都需要获取锁。 - **乐观锁**:假设不会发生冲突,通常使用 CAS 操作。 ...
- 在Java中,“线程”有两层含义: - `java.lang.Thread` 类的一个实例。 - 线程的执行过程。 - 使用 `java.lang.Thread` 类或 `java.lang.Runnable` 接口来定义、实例化和启动新线程。 - 每个Java应用从main()...
"java常用锁使用demo工程"是一个实践项目,旨在帮助开发者理解并熟练掌握Java中的锁机制。在这个工程中,我们可能会看到各种锁的实例,如内置锁(synchronized)、显式锁(java.util.concurrent.locks包中的Lock接口...