`
longgangbai
  • 浏览: 7339181 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

java 中 ReentrantReadWriteLock的读锁和写锁的使用

阅读更多

    jdk文档中关于ReentrantReadWriteLock类使用的一个很好的例子,以下是具体的介绍:
 
 在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下
 是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。  

 

两种锁机制:

1、synchronized

2、ReentrantLock

     ReentrantLock是jdk5的新特性,采用ReentrantLock可以完全替代替换synchronized传统的锁机制,而且采用ReentrantLock的方式更加面向对象,也更加灵活,网上有很多关于对比两者锁方式的文章。


 
 读写锁:ReadWriteLock
 
       在多线程的环境下,对同一份数据进行读写,会涉及到线程安全的问题。比如在一个线程读取数据的时候,另外一个线程在写数据,而导致前后数据的不一致性;一个线程在写数据的时候,另一个线程也在写,同样也会导致线程前后看到的数据的不一致性。
 
       这时候可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读或写操作,而不允许其他线程的读或写操作,这样是可以解决这样以上的问题,但是效率却大打折扣了。因为在真实的业务场景中,一份数据,读取数据的操作次数通常高于写入数据的操作,而线程与线程间的读读操作是不涉及到线程安全的问题,没有必要加入互斥锁,只要在读-写,写-写期 间上锁就行了。
 
 对于这种情况,读写锁则最好的解决方案!
 
 读写锁的机制:
   "读-读"不互斥
   "读-写"互斥
   "写-写"互斥
 
 即在任何时候必须保证:
   只有一个线程在写入;
   线程正在读取的时候,写入操作等待;
   线程正在写入的时候,其他线程的写入操作和读取操作都要等待;
 
 以下是一个缓存类:用于演示读写锁的操作:重入、降级。

 

package com.etrip.concurrent.locks;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 
 * 
 * 两种互斥锁机制:

1、synchronized

2、ReentrantLock

ReentrantLock是jdk5的新特性,采用ReentrantLock可以完全替代替换synchronized传统的锁机制,而且采用ReentrantLock的方式更加面向对象,也更加灵活,网上有很多关于对比两者锁方式的文章,这里就不多口舌了,大家baidu、google一下就水落石出了。在本博客中也写关于这两种锁方式实现的经典例子《生产者消费者》。


 *@author longgangbai
 */
public class CachedData {
	
	// 缓存都应该是单例的,在这里用单例模式设计:
	private static CachedData cachedData = new CachedData();
	private final ReadWriteLock lock = new ReentrantReadWriteLock();//读写锁
	private Map<String, Object> cache = new HashMap<String, Object>();//缓存
	
	private CachedData(){
	}
	
	public static CachedData getInstance(){
		return cachedData;
	}
	
	// 读取缓存:
	public Object read(String key) {
		lock.readLock().lock();
		Object obj = null;
		try {
			obj = cache.get(key);
			if (obj == null) {
				lock.readLock().unlock();
				// 在这里的时候,其他的线程有可能获取到锁
				lock.writeLock().lock();
				try {
					if (obj == null) {
						obj = "查找数据库"; // 实际动作是查找数据库
						// 把数据更新到缓存中:
						cache.put(key, obj);
					}
				} finally {
					// 当前线程在获取到写锁的过程中,可以获取到读锁,这叫锁的重入,然后导致了写锁的降级,称为降级锁。
					// 利用重入可以将写锁降级,但只能在当前线程保持的所有写入锁都已经释放后,才允许重入 reader使用
					// 它们。所以在重入的过程中,其他的线程不会有获取到锁的机会(这样做的好处)。试想,先释放写锁,在
					// 上读锁,这样做有什么弊端?--如果这样做,那么在释放写锁后,在得到读锁前,有可能被其他线程打断。
					// 重入————>降级锁的步骤:先获取写入锁,然后获取读取锁,最后释放写入锁(重点)
					lock.readLock().lock(); 
					lock.writeLock().unlock();
				}
			}
		} finally {
			lock.readLock().unlock();
		}
		return obj;
	}
}

 

 

 

class RWDictionary {

	private final Map<String, String> map = new TreeMap<String, String>();
	private final ReadWriteLock rwl = new ReentrantReadWriteLock();
	private final Lock readLock = rwl.readLock();
	private final Lock writeLock = rwl.writeLock();

	{
	 	map.put("EN", "英国");
    	map.put("CA", "加拿大");
    	map.put("FR", "法国");
    	map.put("CN", "中国");
	}
	
	
	public String get(String key) {
		readLock.lock();
		try {
			return map.get(key);
		} finally {
			readLock.unlock();
		}
	}

	public String[] allKeys() {
		readLock.lock();
		try {
			return (String[]) map.keySet().toArray();
		} finally {
			readLock.unlock();
		}
	}

	public String put(String key, String value) {
		writeLock.lock();
		try {
			return map.put(key, value);
		} finally {
			writeLock.unlock();
		}
	}

	public void clear() {
		writeLock.lock();
		try {
			map.clear();
		} finally {
			writeLock.unlock();
		}
	}
}



public class RWDictionaryTest {
    public static void main(String[] args) {
    	
	    	RWDictionary rw=new RWDictionary();
	    	String value =rw.get("CN");
	    	System.out.println("value ="+value );
    	
    	
	    	Map<String, String> map = new TreeMap<String, String>();
	    	map.put("EN", "英国");
	    	map.put("CA", "加拿大");
	    	map.put("FR", "法国");
	    	map.put("CN", "中国");
    	//Collections.synchronizedMap用法如下
	      Map<String, String> m = Collections.synchronizedMap(map);
	      Set<String> s = m.keySet();  // Needn't be in synchronized block
	       synchronized(m) {  // Synchronizing on m, not s!
	           Iterator<String> i = s.iterator(); // Must be in synchronized block
	           while (i.hasNext()){
	        	   RWDictionaryTest.foo(i.next());
	           }
	       }
	}
    
    public static void foo(String str){
    	System.out.println(" "+ str+" ");
    }
}

 

 

 

分享到:
评论
5 楼 hj01kkk 2015-07-19  
他可能看的例子中下面还要有一个使用数据的片段,类似下面这样

try {
    use(data);
} finally {
    rwl.readLock().unlock();
}
4 楼 liaokangli 2015-04-24  
这段代码有必要的吧!申请读锁之后,其它等待读的线程就可以进行读取,但是等待写的线程就需要等待,结果肯定不一样,他就是不让其他线程马上写
3 楼 FYIHDG 2015-01-08  
2 楼 lastashley 2014-07-15  
@lazy  我也这么觉得,作者能解释下么
1 楼 lazy_ 2014-05-16  
// 读取缓存:  
    public Object read(String key) {  
        lock.readLock().lock();  
        Object obj = null;  
        try {  
            obj = cache.get(key);  
            if (obj == null) {  
                lock.readLock().unlock();  
                // 在这里的时候,其他的线程有可能获取到锁  
                lock.writeLock().lock();  
                try {  
                    if (obj == null) {  
                        obj = "查找数据库"; // 实际动作是查找数据库  
                        // 把数据更新到缓存中:  
                        cache.put(key, obj);  
                    }  
                } finally {  
                    // 当前线程在获取到写锁的过程中,可以获取到读锁,这叫锁的重入,然后导致了写锁的降级,称为降级锁。  
                    // 利用重入可以将写锁降级,但只能在当前线程保持的所有写入锁都已经释放后,才允许重入 reader使用  
                    // 它们。所以在重入的过程中,其他的线程不会有获取到锁的机会(这样做的好处)。试想,先释放写锁,在  
                    // 上读锁,这样做有什么弊端?--如果这样做,那么在释放写锁后,在得到读锁前,有可能被其他线程打断。  
                    // 重入————>降级锁的步骤:先获取写入锁,然后获取读取锁,最后释放写入锁(重点)  
                    lock.readLock().lock();   
                    lock.writeLock().unlock();  
                }  
            }  
        } finally {  
            lock.readLock().unlock();  
        }  
        return obj;  
    }  


这段代码为何要降级锁?直接writeLock.unlock()不就好了?

  // 读取缓存:  
    public Object read(String key) {  
        lock.readLock().lock();  
        Object obj = null;  
        try {  
            obj = cache.get(key);  
        } finally{
            lock.readLock().unlock();  
        }

        if (obj == null) {  
            lock.writeLock().lock();  
            try {  
                if (obj == null) {  
                    obj = "查找数据库"; 
                    cache.put(key, obj);  
                }  
            } finally {  
                lock.writeLock().unlock();  
            }  
        }  
 
        return obj;  
    }  

相关推荐

    java 读写锁代码

    Java 读写锁是Java并发编程中的一种重要机制,它为多线程环境下的数据访问提供了更为精细的控制。在Java的`java.util....通过阅读和分析`readwritelock`这个示例,开发者可以更好地掌握如何在Java中使用读写锁。

    彻底理解Java中的各种锁.pdf

    在Java中,ReentrantReadWriteLock类是读写锁的实现,它包含两个锁:读锁(共享锁)和写锁(独占锁)。读锁可以被多个线程同时持有,而写锁是独占的,当写锁被占用时,其他线程既不能获取读锁也不能获取写锁。 5. ...

    Java 多线程与并发(12-26)-JUC锁- ReentrantReadWriteLock详解.pdf

    ReentrantReadWriteLock包含两种类型的锁:读锁(ReadLock)和写锁(WriteLock),它们可以独立地被获取和释放,从而提高了多线程环境下的并发性能。 1. **为什么需要ReentrantReadWriteLock?** - ...

    8、读写锁ReentrantReadWriteLock&StampLock详解.pdf

    其中`ReentrantReadWriteLock`通过明确区分读锁和写锁来支持并发读操作,而`StampLock`则提供了更灵活的并发控制机制,支持乐观读和悲观读。开发者可以根据具体的应用场景和需求选择合适的锁机制,以达到最佳的并发...

    关于读写锁算法的Java实现及思考

    `ReentrantReadWriteLock`实现了`ReadWriteLock`接口,提供了`readLock()`和`writeLock()`方法,分别用来获取读锁和写锁。 #### 3. 读写锁的自定义实现 在没有`ReadWriteLock`的情况下,可以通过`synchronized`...

    Java的两种读写锁介绍

    - **分离性**:读锁和写锁是分离的,读锁之间不互斥,但读写、写读、写写都是互斥的。 - **公平性**:ReentrantReadWriteLock提供公平和非公平两种模式。公平模式下,线程按照等待时间顺序获取锁;非公平模式下,...

    各种锁汇总,乐观锁、悲观锁、分布式锁、可重入锁、互斥锁、读写锁、分段锁、类锁、行级锁等

    6. **读写锁**:读写锁允许多个读线程同时访问资源,但写线程独占资源。Java的`ReentrantReadWriteLock`提供这种功能,提高并发性能。 7. **分段锁**:当数据结构很大时,如ConcurrentHashMap,采用分段锁策略,将...

    深入浅出ReentrantReadWriteLock源码解析.docx

    在读锁的实现中,ReentrantReadWriteLock支持读锁的重入,而写锁同样支持重入,但写锁具有更高的优先级,即使有多个线程在等待读锁,只要写锁被持有,这些线程都将被阻塞。 此外,ReentrantReadWriteLock还提供了...

    Java 中15种锁的介绍

    5. **读写锁** 包括读锁(共享)和写锁(独占),如`java.util.concurrent.locks.ReentrantReadWriteLock`,提高多线程读操作的并发性能。 6. **乐观锁 / 悲观锁** - **乐观锁** 在读取数据时假定不会有其他线程...

    Java多线程 ReentrantReadWriteLock原理及实例详解

    读写锁通过`ReentrantReadWriteLock`类的实例来创建,该类提供了`readLock()`和`writeLock()`方法,分别用于获取读锁和写锁。 ```java ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Lock readLock...

    java多线程、锁的教程跟案例

    - ReadWriteLock(读写锁):ReentrantReadWriteLock提供了读写分离的锁,允许多个读线程同时访问,但写线程独占资源。 2. **乐观锁** - CAS(Compare and Swap):无锁算法,基于硬件原子操作,检查并更新值,...

    Java锁的种类以及区别

    读写锁通常包括两个锁:读锁(`ReadLock`)和写锁(`WriteLock`)。当多个线程进行读操作时,它们可以同时获得读锁;当有一个线程进行写操作时,其他所有线程(无论是读还是写)都必须等待。 **示例:** - `...

    java中的各种锁详细介绍.docx

    在Java中,锁主要分为两类:乐观锁和悲观锁。 **乐观锁**是一种非阻塞的锁策略,它假设在读取数据时不会有其他线程修改数据,所以在读取时不加锁。当尝试更新数据时,乐观锁会检查在此期间是否有其他线程修改过数据...

    Java 读写锁实现原理浅析

    读写锁分为读锁和写锁,读锁允许多个线程同时读取共享资源,而写锁则独占共享资源以进行写操作。读写锁的规则是:读读不互斥,读写互斥,写写互斥。 读写锁的实现原理 读写锁的实现原理可以通过两个 int 变量来...

    Java分布式应用学习笔记06浅谈并发加锁机制分析

    `ReentrantReadWriteLock`是一种特殊的锁机制,它支持读锁和写锁两种类型,主要用于解决读多写少的场景下的性能问题。当多个线程同时进行读操作时,不会相互阻塞,可以并行执行。但是当一个线程正在执行写操作时,...

    java进阶提高学习教程-14锁机制.pptx

    读写锁维护一对读锁和写锁,通过读和写锁的分离,提升并发性能。Java 中的 ReentrantReadWriteLock 类是 ReadWriteLock 接口的实现类。 CAS CAS 是 Compare And Swap,即比较和交换。CAS 使用一个期望值与一个变量...

    举例说明Java多线程编程中读写锁的使用

    - 在写锁保护下,检查`cacheValid`状态(`@3`行)是为了防止在释放读锁和获取写锁之间的短暂时间窗口内,另一个线程已经完成了写操作。 - 如果数据确实需要更新,执行更新操作,并将`cacheValid`设置为`true`。...

    读写锁_读写锁_

    添加和删除元素(写操作)都使用写锁,而读取元素(读操作)则使用读锁。这样,多个线程可以同时读取列表,但只有单个线程能进行修改操作。 读写锁的设计使得在高并发环境下,系统可以更有效地利用资源。然而,也...

    java常用锁使用demo工程

    - **读写锁(ReadWriteLock)**:ReentrantReadWriteLock是Lock的一个实现,分为读锁(ReadLock)和写锁(WriteLock),允许多个读线程同时访问,但写锁是独占的。 - **条件变量(Condition)**:Lock接口提供了一...

Global site tag (gtag.js) - Google Analytics