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

来源:互联网 发布:识曲软件 编辑:程序博客网 时间:2024/06/04 18:31

初次接触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() {@Overridepublic void run() {while(true)raw.read();}}).start();//线程2,读数据new Thread(new Runnable() {@Overridepublic 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() {@Overridepublic void run() {while(true)raw.read();}}).start();//线程2,写数据new Thread(new Runnable() {@Overridepublic 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() {@Overridepublic void run() {while(true)raw.write();}}).start();//线程2,写数据new Thread(new Runnable() {@Overridepublic 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;}}




0 0
原创粉丝点击