Java多线程之读写锁 ReentrantReadWriteLock
Java多线程之读写锁 ReentrantReadWriteLock
当我们在执行多线程的时候会加上一个***synchronized***让线程安全。比如代码如下:
public class Main {
public static void main(String[] args) {
Runnable r=new Runnable() {
public synchronized void run() {
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(20);
System.out.println(Thread.currentThread().getName()+ ":正在进行读操作……");
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":读操作完毕!");
}
};
new Thread(r).start();
new Thread(r).start();
}
}
从上面代码可以看出在加了synchronized关键字之后,读与读之间,也是互斥的,也就是说,必须等待Thread-0读完之后,才会轮到Thread-1线程读,而无法做到同时读文件,这种情况在大量线程同时都需要读文件的时候,读写锁的效率,明显要高于synchronized关键字的实现。而读写锁可以同时执行读操作,但是只能一个线程执行写的操作。读锁和写锁之间是互斥的,读锁和写锁联合在一起用才能起作用, 如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。
代码实现如下:
package com.hzl.java.Thread;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Main11 {
private static String[] box = new String[1];
private static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " 准备载入读锁");
rwl.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 读锁加载完毕");
if (box[0] == null) {
System.out.println(Thread.currentThread().getName() + " 准备卸载读锁");
rwl.readLock().unlock();
System.out.println(Thread.currentThread().getName() + " 读锁卸载完毕");
System.out.println(Thread.currentThread().getName() + " 准备载入写锁");
rwl.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " 写锁载入完毕");
if (box[0] == null) { // 防止后边线程加载数据,使用双端检测机制
box[0] = String.valueOf(Math.random());
}
rwl.writeLock().unlock();
System.out.println(Thread.currentThread().getName() + " 写锁卸载完毕");
rwl.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 读锁卸载完毕");
} else {
System.out.println(Thread.currentThread().getName() + box[0]);
}
rwl.readLock().unlock();
System.out.println(Thread.currentThread().getName() + " 写锁载入完毕");
}
};
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
}
执行结果如下:有三种情况
结果分析:当三个线程开始执行时,一开始三个线程同时进到“准备载入读锁”,线程0抢先执行到“卸载读锁完毕”,“准备载入写锁”,让出读锁,线程1抢到读锁,执行到“卸载读锁”,线程2抢到读锁,执行到读锁卸载完毕时,线程2又抢到写锁,此时box[0] ==null,所以给box[0]赋值,执行完毕后,让出写锁,此时线程0抢到写锁,box[0] != null,所以不赋值,线程执行完毕,让出写锁,线程1抢到写锁,box[0] != null,所以不赋值执行完毕之后,线程1抢到读锁,然后释放读锁,线程2抢到读锁,释放读锁,线程0抢到读锁,释放读锁。
结果分析:线程开始执行时,线程2和线程0开始准备载入读锁,线程2抢到读锁,当线程2执行到完毕,让出读锁,线程1开始准备载入读锁,线程2又抢到写锁,此时box[0]==null,所以给box[0] 赋值。执行完毕后,让出写锁,此时线程1抢到读锁,此时的box[0] !=null,所以直接输出box[0]的值,让出读锁,此时线程0抢到读锁,情况和线程1相同,所以让出读锁,此时线程2在执行到“写锁卸载完毕”后抢到读锁,然后又释放读锁。
结果分析:线程开始时,线程0 和线程1进入准备载入读锁,此时线程0先抢到线程执行权,先拿到读锁,此时线程1又抢到线程执行权,拿到读锁,执行完毕后,释放读锁,此时线程2开始执行,准备载入读锁,线程1抢到写锁,此时box[0] ==null,所以给box[0]赋值,执行完毕卸载写锁,然后又拿到读锁,释放读锁。此时线程2拿到读锁,此时box[0] !=null ,所以直接输出box[0]的值。让出读锁,线程执行完。此时线程0在拿到写锁,此时的box[0]!=null,所以不赋值,释放写锁,在拿到读锁,卸载读锁。