ReentrantReadWriteLock读写锁的使用1
本文可作为传智播客《张孝祥-Java多线程与并发库高级应用》的学习笔记。
一个简单的例子
两个线程,一个不断打印a,一个不断打印b
public class LockTest {
public static void main(String[] args){
final Outputer outputer = new Outputer();
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
}//a的数量与b的数量一致
}
}).start();
}
static class Outputer{
public void output(String name){
int len = name.length();
try{
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}finally{
}
}
}
}
最后的部分结果
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
b
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
为什么会这样?
很简单,在输出b的时候,还没有输出完,a线程(打印a的那个线程)已经抢到了控制权,开始打印a,等a线程将a输出完后,并且打印了一个回车后,b线程才抢回系统控制权,打印它上一次最后剩下的一个b。
要解决上面的问题很简单:
static class Outputer{
public synchronized void output(String name){
int len = name.length();
//.....
}
}
这样一来,我们就保证了Outputer类里的output方法是原子性的,不会有两个线程同时执行它。
就上面的例子而言我们是否还有更好的方法呢?
有。
java5中提供了一种更加面向对象的技术类解决多线程之间的互斥问题-----锁。
java.util.concurrent.locks Interface Lock
锁技术的核心就是Lock及它的实现类。
基本锁
上面的例子如果使用锁,代码如下
static class Outputer{
Lock lock = new ReentrantLock();
public void output(String name){
int len = name.length();
lock.lock(); //标识1
try{
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}finally{
lock.unlock(); //标识2
}
}
线程a执行到上面代码的标识1处加锁,当线程a在输出字符a时,线程b也执行到了标识1处。此时线程b是不能获得锁的。它被阻塞到标识1处,直到线程a打印完之后在标识2处释放了锁。(线程a线程b共用一把锁,也就是Lock lock = new ReentrantLock())
另外为什么标识2出的释放锁放到了finally里,大家应该明白了吧。
读与写
上面的问题中output的主体(len是方法内部的局部变量,为每个线程自有,互不干涉)被全部互斥,它保证了任何时候,都只有一个线程执行标识1与标识2直接的代码。
但是我们得意识到:对共有数据的操作,基本可以分为两类,读与写。
对共有资源操作的时候,我们应该遵循三大准则:
1 当一个线程对资源进行写操作的时候,别的线程既不能对资源读也不能对资源写。
2 当一个线程对资源进行读操作的时候,别的线程不能对资源写。
3 当一个线程对资源进行读操作的时候,别的线程能对资源读。
一二准则保证了系统的正确性。第三准则能提高系统的性能。 毕竟多个线程对资源进行读操作是可以的。
看下面这个既有读又有写的例子。
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue3 q3 = new Queue3();
for(int i=0;i<3;i++)
{
new Thread(){
public void run(){
while(true){
q3.get();
}
}
}.start();
new Thread(){
public void run(){
while(true){
q3.put(new Random().nextInt(10000));
}
}
}.start();
}
}
}
class Queue3{
private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
public void get(){
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void put(Object data){
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep((long)(Math.random()*1000));
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果如下
Thread-0 be ready to read data!
Thread-1 be ready to write data!
Thread-2 be ready to read data!
Thread-3 be ready to write data!
Thread-4 be ready to read data!
Thread-5 be ready to write data!
Thread-0have read data :null
Thread-0 be ready to read data!
Thread-3 have write data: 5280
Thread-3 be ready to write data!
Thread-1 have write data: 5839
Thread-1 be ready to write data!
Thread-4have read data :5839
我们可以看到 读中有写 写中有写 写中有读 完全乱套了。
我们试试个两个方法加上synchronized 结果如下
Thread-0 be ready to read data!
Thread-0have read data :null
Thread-5 be ready to write data!
Thread-5 have write data: 7931
Thread-5 be ready to write data!
Thread-5 have write data: 9564
Thread-5 be ready to write data!
Thread-5 have write data: 1203
Thread-5 be ready to write data!
Thread-5 have write data: 8870
Thread-4 be ready to read data!
Thread-4have read data :8870
Thread-3 be ready to write data!
Thread-3 have write data: 9334
Thread-3 be ready to write data!
Thread-3 have write data: 2680
Thread-3 be ready to write data!
Thread-3 have write data: 9948
Thread-3 be ready to write data!
Thread-3 have write data: 375
Thread-2 be ready to read data!
读与写完全互斥,读的时候不写,写的时候不读。满足一二准则。
读写锁
为了实现准则三,在java5中的出现了读写锁。
java.util.concurrent.locks Interface ReadWriteLock
ReadWriteLock有两个方法
Lock readLock() Returns the lock used for reading.
Lock writeLock() Returns the lock used for writing.
得到两种锁后,就可以调用锁的lock与unlock方法了。
一般使用它的子类ReentrantReadWriteLock来产生ReadWriteLock
其签名如下:
public class ReentrantReadWriteLock extends Object implements ReadWriteLock, Serializable
看看使用方法
class Queue3{
private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
ReadWriteLock rwl = new ReentrantReadWriteLock();
public void get(){
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep(20);
System.out.println(Thread.currentThread().getName() + " have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock();
}
}
public void put(Object data){
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep(20);
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.writeLock().unlock();
}
}
}
结果如下
Thread-5 have write data: 7329
Thread-0 be ready to read data!
Thread-0 have read data :7329
Thread-1 be ready to write data!
Thread-1 have write data: 1361
Thread-2 be ready to read data!
Thread-4 be ready to read data!
Thread-0 be ready to read data!
Thread-2 have read data :1361
Thread-2 be ready to read data!
Thread-4 have read data :1361
我们可以看到 线程1的写是完全互斥的。
而线程2 4 0的读是可以同步进行的。
这是读写锁最简单的例子,下一节,我们看一个稍微复杂的,把读锁与写锁放到一个方法内的例子。
感谢glt
ReentrantReadWriteLock读写锁的使用1的更多相关文章
- ReentrantReadWriteLock读写锁的使用
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
- ReentrantReadWriteLock读写锁的使用2
本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 这一节我们做一个缓存系统. 在读本节前 请先阅读 ReentrantReadWriteLock读写锁的使用1 第一 ...
- 锁对象-Lock: 同步问题更完美的处理方式 (ReentrantReadWriteLock读写锁的使用/源码分析)
Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...
- Java并发包源码学习系列:ReentrantReadWriteLock读写锁解析
目录 ReadWriteLock读写锁概述 读写锁案例 ReentrantReadWriteLock架构总览 Sync重要字段及内部类表示 写锁的获取 void lock() boolean writ ...
- ReentrantReadWriteLock读写锁简单原理案例证明
ReentrantReadWriteLock存在原因? 我们知道List的实现类ArrayList,LinkedList都是非线程安全的,Vector类通过用synchronized修饰方法保证了Li ...
- ReentrantReadWriteLock读写锁详解
一.读写锁简介 现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁.在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源:但是如果一个线 ...
- java多线程:并发包中ReentrantReadWriteLock读写锁的锁降级模板
写锁降级为读锁,但读锁不可升级或降级为写锁. 锁降级是为了让当前线程感知到数据的变化. //读写锁 private ReentrantReadWriteLock lock=new ReentrantR ...
- java中ReentrantReadWriteLock读写锁的使用
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
- java多线程:ReentrantReadWriteLock读写锁使用
Lock比传统的线程模型synchronized更多的面向对象的方式.锁和生活似,应该是一个对象.两个线程运行的代码片段要实现同步相互排斥的效果.它们必须用同一个Lock对象. 读写锁:分为读锁和写锁 ...
随机推荐
- Apache shiro集群实现 (一) shiro入门介绍
近期在ITOO项目中研究使用Apache shiro集群中要解决的两个问题,一个是Session的共享问题,一个是授权信息的cache共享问题,官网上给的例子是Ehcache的实现,在配置说明上不算很 ...
- 源码推荐:移动端商城(微信小程序源代码) WebView离线缓存
移动端商城(微信小程序源代码)(上传者:腾讯攻城师jack) 功能包括:商品橱窗,商品搜索,购物车,结账等功能. TableView嵌套webView自适应高度(上传者:linlinchen) tab ...
- android 网络获取json并且显示(2)
1.将要的取得的json数据格式如下: 我们封装之前的类用google提供的JSONArray和JSONObject类对json字符串进行解析. 对于姚明显示每一条数据,我们封装了一个类如下: pub ...
- studio中碰到的jni问题:java.lang.UnsatisfiedLinkError
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52606328 studio中碰到 ...
- 内存数据网格IMDG简介
1 简介 将内存作为首要存储介质不是什么新鲜事儿,我们身边有很多主存数据库(IMDB或MMDB)的例子.在对主存的使用上,内存数据网格(In Memory Data Grid,IMDG)与IMDB类似 ...
- Dynamics CRM2015 页面导航栏顶部全局快速查找功能配置
在CRM2015中微软加入了新的快速查找功能,让你的数据查找更加方便,功能栏如下图所示,直接可以框中输入搜索项进行搜索. 但该功能是需要进行些配置,具体的配置在设置-管理-系统设置中,默认的就是红框中 ...
- 03_dbcp数据源依赖jar包,DBCP中API介绍,不同过dbcp方式使用dbcp数据库连接池,通过配置文件使用dbcp数据库连接池
DBCP数据源 使用DBCP数据源,需要导入两个jar包 Commons-dbcp.jar:连接池的实现 Common-pool.jar:连接池实现的依赖库. 导入mysql的jar包. DBC ...
- 对N各集合中的任意元素进行排列组合问题
小李去市场买菜,有蔬菜(茄子.黄瓜.大白菜...等k中素菜),和肉类(牛肉,羊肉,鸡肉...等m种荤菜),及点心(麻饼,桃酥,枣花...等n中点心),现在老婆要求每天一荤一素一点心 并且每天的样式要尽 ...
- 详解EBS接口开发之采购申请导入
更多内容可以参考我的博客 详解EBS接口开发之采购订单导入 http://blog.csdn.net/cai_xingyun/article/details/17114697 /*+++++++ ...
- 【安卓中的缓存策略系列】安卓缓存策略之综合应用ImageLoader实现照片墙的效果
在前面的[安卓缓存策略系列]安卓缓存之内存缓存LruCache和[安卓缓存策略系列]安卓缓存策略之磁盘缓存DiskLruCache这两篇博客中已经将安卓中的缓存策略的理论知识进行过详细讲解,还没看过这 ...