Java线程总结(八):并发包------读写锁ReadWriteLock的简单例子详细理解

本文通过具体实例解释了ReadWriteLock的三个核心特性:读锁与读锁的并行性、读锁与写锁的互斥性以及写锁与写锁的互斥性。并通过一个缓存类的实现展示了ReadWriteLock在实际应用中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初次接触ReadWriteLock类时也在网上查了很多资料,很容易了解到ReadWriteLock是读写锁,并且读写锁的机制有以下三个特点:

  读锁---读锁    (不互斥)

  读锁---写锁     (互斥)

  写锁---写锁     (互斥)

什么意思呢?

网上很多资料,直接用这三个特点实现一个缓存的例子进行了讲解,但是对小白来说还有那么一丝丝的迷惑(老鸟忽略),下面就逐一演示:

1. 读锁---读锁

main方法里两个线程都在读数据,在read()方法里已经上了读锁且没有解锁(为了测试)

public class ReadWriteLockTest {
	public static void main(String[] args) {
		final ReadAndWrite raw = new ReadAndWrite();
		raw.map.put("data", 1);
		//线程1,读数据
		new Thread(
				new Runnable() {
					@Override
					public void run() {
						while(true)
							raw.read();						
					}
		}).start();
		//线程2,读数据
		new Thread(
				new Runnable() {
					@Override
					public void run() {
						while(true)
							raw.read();
					}
		}).start();
		
	}
}


class ReadAndWrite{
	ReadWriteLock rwlock = new ReentrantReadWriteLock();//读写锁
	Map map = new HashMap();//共享的数据
	public void read(){
		rwlock.readLock().lock();//上读锁,且没有解锁
		System.out.println(Thread.currentThread().getName()+"读开始...");
		System.out.println(Thread.currentThread().getName()+"读数据为:"+map.get("data"));
		System.out.println(Thread.currentThread().getName()+"读结束...");
		try {
			Thread.sleep((long) (Math.random()*1000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

(可以看到两个线程都能不断读数据,记住这个现象,接着看下面的例子进行对比)

运行结果:

Thread-0读开始...
Thread-0读数据为:1
Thread-0读结束...
Thread-1读开始...
Thread-1读数据为:1
Thread-1读结束...
Thread-1读开始...
Thread-1读数据为:1
Thread-1读结束...
Thread-0读开始...
Thread-0读数据为:1
Thread-0读结束...

2. 读锁---写锁 

main方法里线程1在读数据,线程2在写数据,将read()方法里的“解读锁”注释掉和不注释分别运行对比结果

public class ReadWriteLockTest2 {
	public static void main(String[] args) {
		final ReadAndWrite raw = new ReadAndWrite();
		raw.map.put("data", 1);
		//线程1,读数据
		new Thread(
				new Runnable() {
					@Override
					public void run() {
						while(true)
							raw.read();						
					}
		}).start();
		//线程2,写数据
		new Thread(
				new Runnable() {
					@Override
					public void run() {
						while(true)
							raw.write();
					}
		}).start();
		
	}
}

class ReadAndWrite{
	ReadWriteLock rwlock = new ReentrantReadWriteLock();//读写锁
	Map map = new HashMap();//共享的数据
	public void read(){
		rwlock.readLock().lock();//上读锁
		System.out.println(Thread.currentThread().getName()+"读开始...");
		System.out.println(Thread.currentThread().getName()+"读数据为:"+map.get("data"));
		System.out.println(Thread.currentThread().getName()+"读结束...");
		<span style="color:#ff0000;">//rwlock.readLock().unlock();//解读锁</span>
		try {
			Thread.sleep((long) (Math.random()*1000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	public void write(){
		rwlock.writeLock().lock();//上写锁
		System.out.println(Thread.currentThread().getName()+"写开始...");
		double data = Math.random();
		map.put("data", data);
		System.out.println(Thread.currentThread().getName()+"写数据为:"+data);
		System.out.println(Thread.currentThread().getName()+"写结束...");
		rwlock.writeLock().unlock();//解写锁
		try {
			Thread.sleep((long) (Math.random()*1000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

read()方法里“解读锁”注释掉,(可以看到只有线程1在读数据,线程2是阻塞状态无法写数据,证明了读锁和写锁的互斥

运行结果:

Thread-0读开始...
Thread-0读数据为:1
Thread-0读结束...
Thread-0读开始...
Thread-0读数据为:1
Thread-0读结束...

read()方法里“解读锁”不注释,(可以看到线程1和线程2交替的读和写数据,也就是读锁解了,其他线程就可以写数据了,写锁解了,其他线程就可以读数据了

运行结果:

Thread-0读开始...
Thread-0读数据为:1
Thread-0读结束...
Thread-1写开始...
Thread-1写数据为:0.4387383401358649
Thread-1写结束...

3. 写锁---写锁

相信看了上面两个例子,基本就明白了写锁和写锁是怎么互斥的,同样线程1和线程2都进行写数据,write()方法上写锁并且不解锁(测试才这样写的)

public class ReadWriteLockTest3 {
	public static void main(String[] args) {
		final ReadAndWrite raw = new ReadAndWrite();
		raw.map.put("data", 1);
		//线程1,写数据
		new Thread(
				new Runnable() {
					@Override
					public void run() {
						while(true)
							raw.write();						
					}
		}).start();
		//线程2,写数据
		new Thread(
				new Runnable() {
					@Override
					public void run() {
						while(true)
							raw.write();
					}
		}).start();
		
	}
}

class ReadAndWrite{
	ReadWriteLock rwlock = new ReentrantReadWriteLock();//读写锁
	Map map = new HashMap();//共享的数据
	public void write(){
		rwlock.writeLock().lock();//上写锁
		System.out.println(Thread.currentThread().getName()+"写开始...");
		double data = Math.random();
		map.put("data", data);
		System.out.println(Thread.currentThread().getName()+"写数据为:"+data);
		System.out.println(Thread.currentThread().getName()+"写结束...");
		try {
			Thread.sleep((long) (Math.random()*1000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
(write()方法里上了写锁并且没有解锁,所以只有一个线程在写数据,其他线程无法进行写数据,你也可以把解锁给加上,运行一下对比看看)
运行结果:

Thread-0写开始...
Thread-0写数据为:0.2439106501627466
Thread-0写结束...
Thread-0写开始...
Thread-0写数据为:0.44688857423885386
Thread-0写结束...
Thread-0写开始...

相信通过以上的代码和运行结果进行对比和观察,应该已经理解了ReadWriteLock三个特点,下面贴上实现缓存类的代码(jdk中ReentrantReadWriteLock类的一个例子):


public class Cache {
	private Map<String, Object> cache = new HashMap<String, Object>();
	private ReadWriteLock rwLock = new ReentrantReadWriteLock();

	public Object getData(String key) {
		// 首先上读锁
		rwLock.readLock().lock();
		// 首先从缓存中获取
		Object value = null;
		try {
			Thread.sleep(1000);
			value = cache.get(key);
			if (value == null) {
				// 如果缓存中没有数据,那么就从数据库中获取
				// 但此时需要上写锁,只需要让一个进程进行写数据
				// 首先去除读锁,然后加上写锁
				rwLock.readLock().unlock();
				rwLock.writeLock().lock();
				try {
					// 注意防止多线程运行到上一步,某个线程写完数据后
					// 别的线程就需要看是否有数据再决定是否进行写操作
					// 在写之前再读一次,防止最开始的线程都进行写操作</span>
					value = cache.get(key);
					// 第一个线程写完后,防止后面的线程再次写数据
					if (value == null) {
						System.out.println("有线程写数据........");
						value = "数据库中获取";
						// 将数据放入缓存
						cache.put(key, value);
						System.out.println("数据写完了.......");
					}
				} finally {
					rwLock.readLock().lock();// 恢复读锁,锁的重入
					rwLock.writeLock().unlock();
				}
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			rwLock.readLock().unlock();// 解读锁
		}
		return value;
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值