多线程与并发----读写锁

    1、锁分为读锁和写锁

    2、多个读锁不互斥

    3、读锁和写锁互斥

    4、写锁与写锁互斥

    总之,这是由JVM自己控制的,如果你的代码只读取数据,可以多人同时读,但不能同时写,那就上读锁;若歌你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。


三个线程读数据,三个线程写数据示例:

        功能:可以同时读,读的时候不能写,不能同时写,写的时候不能读,读的时候上读锁,读完解锁;写的时候上写锁,写完解锁。注意finally解锁

import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockTest {

	/**读写所使用
	* 三个线程读,三个线程写
	*/
	public static void main(String[] args) {
		//共享对象
		final Source source = new Source();
		//创建线程
		for (int i=0; i<3; i++){
			//读
			new Thread(new Runnable(){
				public void run(){
					while (true)
						source.get();
				}
			}).start();
			//写
			new Thread(new Runnable(){
				public void run(){
					while (true)
						source.put(new Random().nextInt(999));
				}
			}).start();
		}
	}
	static class Source
	{
		//共享数据
		private int data = 0;
		//要操作同一把锁上的读或写锁
		ReadWriteLock rwl = new ReentrantReadWriteLock();
		
		//读方法
		public void get()
		{
			//上读锁
			rwl.readLock().lock();
			try
			{
				//获取数据并输出
				System.out.println("读——"+Thread.currentThread().getName()+"正在获取数据。。。");
				try
				{
					Thread.sleep(new Random().nextInt(6)*1000);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				System.out.println("读——"+Thread.currentThread().getName()+"获取到的数据:"+data);
			}finally
			{
				//解锁
				rwl.readLock().unlock();
			}			
		}
		//写方法
		public void put(int data)
		{
			//上写锁
			rwl.writeLock().lock();
			try
			{
				//提示信息
				System.out.println("写——"+Thread.currentThread().getName()+"正在改写数据。。。");
				try
				{
					Thread.sleep(new Random().nextInt(6)*1000);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				this.data = data;
				System.out.println("写——"+Thread.currentThread().getName()+"已将数据改写为:"+data);
			}finally
			{
				//解锁
				rwl.writeLock().unlock();
			}			
		}
	}
}

    运行结果为:

读——Thread-0正在获取数据。。。
读——Thread-2正在获取数据。。。
读——Thread-4正在获取数据。。。
读——Thread-0获取到的数据:0
读——Thread-4获取到的数据:0
读——Thread-2获取到的数据:0
写——Thread-3正在改写数据。。。
写——Thread-3已将数据改写为:565
写——Thread-3正在改写数据。。。


ReentrantReadWriteLock

构造方法摘要

ReentrantReadWriteLock()           使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock

ReentrantReadWriteLock(boolean fair)           使用给定的公平策略创建一个新的 ReentrantReadWriteLock

方法摘要

protected  Thread

getOwner()           返回当前拥有写入锁的线程,如果没有这样的线程,则返回 null

protected  Collection<Thread>

getQueuedReaderThreads()           返回一个 collection,它包含可能正在等待获取读取锁的线程。

protected  Collection<Thread>

getQueuedThreads()           返回一个 collection,它包含可能正在等待获取读取或写入锁的线程。

protected  Collection<Thread>

getQueuedWriterThreads()           返回一个 collection,它包含可能正在等待获取写入锁的线程。

 int

getQueueLength()           返回等待获取读取或写入锁的线程估计数目。

 int

getReadHoldCount()           查询当前线程在此锁上保持的重入读取锁数量。

 int

getReadLockCount()           查询为此锁保持的读取锁数量。

protected  Collection<Thread>

getWaitingThreads(Condition condition)           返回一个 collection,它包含可能正在等待与写入锁相关的给定条件的那些线程。

 int

getWaitQueueLength(Condition condition)           返回正等待与写入锁相关的给定条件的线程估计数目。

 int

getWriteHoldCount()           查询当前线程在此锁上保持的重入写入锁数量。

 boolean

hasQueuedThread(Thread thread)           查询是否给定线程正在等待获取读取或写入锁。

 boolean

hasQueuedThreads()           查询是否所有的线程正在等待获取读取或写入锁。

 boolean

hasWaiters(Condition condition)           查询是否有些线程正在等待与写入锁有关的给定条件。

 boolean

isFair()           如果此锁将公平性设置为 ture,则返回 true

 boolean

isWriteLocked()           查询是否某个线程保持了写入锁。

 boolean

isWriteLockedByCurrentThread()           查询当前线程是否保持了写入锁。

 ReentrantReadWriteLock.ReadLock

readLock()           返回用于读取操作的锁。

 String

toString()           返回标识此锁及其锁状态的字符串。

 ReentrantReadWriteLock.WriteLock

writeLock()           返回用于写入操作的锁。

JDK帮助文档中的示例用法。下面的代码展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理): 

class CachedData {

   Object data;

   volatile boolean cacheValid;	数据有没有标记

   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {处理数据

     rwl.readLock().lock();先上读锁

     if (!cacheValid) {如果数据不存在

        // Must release read lock before acquiring write lock

        rwl.readLock().unlock();准备写数据,需先解除读锁

        rwl.writeLock().lock();上写锁

        // Recheck state because another thread might have acquired

        //   write lock and changed state before we did.

        if (!cacheValid) {再次检查数据是否存在,防止其他线程已经存入数据

          data = ...

          cacheValid = true;写好数据,改变标记

        }

        // Downgrade by acquiring read lock before releasing write lock

        准备释放写锁,数据存在了,释放后就要使用数据,恢复产生数据前的读锁状态

rwl.readLock().lock();

        rwl.writeLock().unlock(); // Unlock write, still hold read

     }

 

     use(data);存在直接使用数据

     rwl.readLock().unlock();解除读锁

   }

 }


面试题:设计一个缓存系统

缓存系统:你要取数据,需调用我的public Object getData(String key)方法,我要检查我内部有没有这个数据,如果有就直接返回,如果没有,就从数据库中查找这个数,查到后将这个数据存入我内部的存储器中,下次再有人来要这个数据,我就直接返回这个数不用再到数据库中找了。 你要取数据不要找数据库,来找我。

class CachedSystem
{	
	//缓存系统的存储器
	private Map<String, Object> cache = new HashMap<String, Object>();
	//取数据方法可能有多个线程来取数据,没有数据的话又会去数据库查询,需要互斥
	
	public synchronized Object get(String key)
	{	
		//先查询内部存储器中有没有要的值
	
		Object value = cache.get(key);
		if (value==null)//如果没有,就去数据库中查询,并将查到的结果存入内部存储器中
		{	
			value = “aaaa”; //实际代码是查询后的结果 queryDB(key)	
			cache.put(key, value);	
		}	
		return value;
	}
	
	//上面的代码每次只能有一个线程来查询,但只有写的时候才需要互斥,修改如下
	
	//来一个读写锁	
	ReadWriteLock rwl = new ReentrantReadWriteLock();	
	public Object get(String key){
		//上读锁	
		rwl.readLock().lock();	
		//先查询内部存储器中有没有要的值	
		Object value = cache.get(key);	
		if (value==null)//如果没有,就去数据库中查询,并将查到的结果存入内部存储器中	
		{	
			//释放读锁  上写锁	
			rwl.readLock().unlock();	
			rwl.writeLock().lock();	
			if (value==null)再次进行判断,防止多个写线程堵在这个地方重复写		
			{		
				value = “aaaa”;		
				cache.put(key, value);	
			}
		//设置完成 释放写锁,恢复读写状态
		
		rwl.readLock().lock();	
		rwl.writeLock().unlock();	
		}
	//释放读锁
	rwl.readLock().unlock();
	return value;	
	//注意:try finally中unlock
	}
}

猜你喜欢

转载自blog.csdn.net/pengzhisen123/article/details/80293267