一.序言:
类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务,这样做虽然保证了实例变量的线程安全性,单效率却是非常低的.所以在JDK中提供了一种读写锁ReentrantReadWriteLock类,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁ReentrantReadLock来提升该方法的代码运行速度。
读写锁表示也有两个锁,一个是读操作相关的锁,也成为共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁之间互斥,写锁与写锁互斥,在没有现成Thread进行写入操作时,进行读取操作的多个Thread都可以获取锁,而进行写入操作的Thread只有在获取写锁后才能进行写入操作,即多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。
二.读读锁demo演示:
1.创建一个功能类,在类中调用ReentrantReadWriteLock读锁的方法
/**
* Created by ${ligh} on 2019/7/9 上午8:48
*/
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read(){
try{
try{
lock.readLock().lock();
System.out.println("获得读锁 "+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.readLock().unlock();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
2.分别创建线程A和线程B进行读操作
线程A:
/**
* Created by ${ligh} on 2019/7/9 上午8:53
*/
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service){
super();
this.service=service;
}
@Override
public void run() {
service.read();
}
}
线程B:
/**
* Created by ${ligh} on 2019/7/9 上午8:54
*/
public class ThreadB extends Thread{
private Service service;
public ThreadB(Service service){
super();
this.service=service;
}
@Override
public void run() {
service.read();
}
}
3.测试读读锁:
/**
* Created by ${ligh} on 2019/7/9 上午8:48
*/
public class TestReadLock {
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.setName("A");
ThreadB threadB = new ThreadB(service);
threadB.setName("B");
threadA.start();
threadB.start();
}
}
4.测试结果:
总结:
通过打印结果来看,两个线程几乎同时进入lock()方法后面的代码,说明了lock.readLock()读锁可以提高程序运行效率,允许多个线程 同时执行lock()方法后面的代码。
三.写写锁demo演示:
1.创建一个功能类,调用ReentrantReadWriteLock写锁的方法
/**
* Created by ${ligh} on 2019/7/9 上午9:13
*/
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void write(){
try{
try{
lock.writeLock().lock();
System.err.println("获得写锁 "+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(1000);
}finally {
lock.writeLock().unlock();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
2.分别创建线程A,B调用写操作:
线程A:
/**
* Created by ${ligh} on 2019/7/9 上午8:53
*/
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service){
super();
this.service=service;
}
@Override
public void run() {
service.write();
}
}
线程B:
/**
* Created by ${ligh} on 2019/7/9 上午8:54
*/
public class ThreadB extends Thread{
private Service service;
public ThreadB(Service service){
super();
this.service=service;
}
@Override
public void run() {
service.write();
}
}
3.创建测试类:
/**
* Created by ${ligh} on 2019/7/9 上午9:18
*/
public class TestWriteLock {
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.setName("A");
ThreadB threadB = new ThreadB(service);
threadB.setName("B");
threadA.start();
threadB.start();
}
}
4.测试结果:
总结:
通过打印结果显示: 使用写锁代码lock.writeLock()的效果就是同一时间只允许一个线程执行lock()后面的代码,也就是互斥了。
四.读写锁demo演示:
1.创建一个功能类,调用ReentrantReadWriteLock读锁和写锁的方法
/**
* Created by ${ligh} on 2019/7/9 上午9:25
*/
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**
* 读锁
*/
public void read(){
try{
try{
lock.readLock().lock();
System.err.println("获得读锁 "+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.readLock().unlock();
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 写锁
*/
public void write(){
try{
try{
lock.writeLock().lock();
System.err.println("获得写锁 "+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(10000);
}finally {
lock.writeLock().unlock();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
2.创建线程A调用读锁,线程B调用写锁
线程A:
/**
* Created by ${ligh} on 2019/7/9 上午9:30
*
* 线程A调用读锁
*/
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service){
super();
this.service=service;
}
@Override
public void run() {
service.read();
}
}
线程B:
/**
* Created by ${ligh} on 2019/7/9 上午9:32
*/
public class ThreadB extends Thread{
private Service service;
public ThreadB(Service service){
super();
this.service = service;
}
@Override
public void run() {
service.write();
}
}
3.创建测试类:
/**
* Created by ${ligh} on 2019/7/9 上午9:33
*/
public class TestReadWriteLock {
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.setName("A");
ThreadB threadB = new ThreadB(service);
threadB.setName("B");
threadA.start();
threadB.start();
}
}
4.测试结果:
4.总结:
1).通过打印结果显示: “读写"操作是互斥的,而且下一个示例说明"写读"操作也是互斥的,即只要出现"写操作"的过程,就是互斥的,
2).通过测试"读写”,"写读"和"写写"都是互斥的;而"读读"是异步的,非互斥的
3).通过测试可以使用Lock对象将synchronized关键字替换掉,而且其具有的独特功能也是synchronized所不具有的,在学习并发时,Lock是synchronized关键字的进阶,掌握Lock有助于学习并发包中源代码的实现原理,在并发包中大量的类使用了Lock接口作为同步的处理方式。
个人心得:
最近一直在研究多线程,并且发现里面的东西实际没有那么难,尤其是研究实现原理之后,看JDK的源码就轻松了很多。