【Java】读写锁 ReadWriteLock接口

和被synchronized修饰的对象同时只能被一个线程访问不同,ReadWriteLock接口提供了更细粒度锁机制。ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持,但是写入锁是独占的。

下面是测试代码:

import org.junit.Test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {

    @Test
    public void test1() throws InterruptedException {
        Data data = new Data();
        Worker t1 = new Worker(data, true);//读取数据
        Worker t2 = new Worker(data, true);//读取数据
        t1.start();
        t1.join();
        t2.start();
        t2.join();
    }

    @Test
    public void test2() throws InterruptedException {
        Data data = new Data();
        Worker t1 = new Worker(data, true);//读取数据
        Worker t2 = new Worker(data, false);//设置数据
        t1.start();
        t1.join();
        Thread.sleep(1000);
        t2.start();
        t2.join();
    }

    @Test
    public void test3() throws InterruptedException {
        Data data = new Data();
        Worker t1 = new Worker(data, false);//设置数据
        Worker t2 = new Worker(data, true);//读取数据
        t1.start();
        t1.join();
        Thread.sleep(1000);
        t2.start();
        t2.join();
    }

    @Test
    public void test4() throws InterruptedException {
        Data data = new Data();
        Worker t1 = new Worker(data, false);//设置数据
        Worker t2 = new Worker(data, false);//设置数据
        t1.start();
        t1.join();
        Thread.sleep(1000);
        t2.start();
        t2.join();
    }
}

class Data {
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    ReadWriteLock lock = new ReentrantReadWriteLock();
    Lock read = lock.readLock();
    Lock write = lock.writeLock();

    public void set() {
        write.lock();
        System.out.println(Thread.currentThread().hashCode() + " set:begin " + sdf.format(new Date()));
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
        } finally {
            System.out.println(Thread.currentThread().hashCode() + " set:end "+ sdf.format(new Date()));
            write.unlock();
        }
    }

    public int get() {
        read.lock();
        System.out.println(Thread.currentThread().hashCode() + " get :begin " + sdf.format(new Date()));
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
        } finally {
            System.out.println(Thread.currentThread().hashCode() + " get :end " + sdf.format(new Date()));
            read.unlock();
        }
        return 1;
    }
}

class Worker extends Thread {
    Data data;
    boolean read;

    public Worker(Data data, boolean read) {
        this.data = data;
        this.read = read;
    }

    public void run() {
        if (read){
            data.get();
        }
        else{
            data.set();
        }
    }
}

Run test1()  输出结果如下:

751872434 get :begin 2016-05-04 14:53:49.612
1431398495 get :begin 2016-05-04 14:53:49.613
751872434 get :end 2016-05-04 14:53:54.613
1431398495 get :end 2016-05-04 14:53:54.613

Process finished with exit code 0

结果分析:两个线程读取数据的时间是有重合部分的,说明读取锁readLock可以有多个线程同时保持,执行读取锁后,数据还可被其他线程读取。

Run test2()  输出结果如下:

2034193582 get :begin 2016-05-04 15:03:41.996
2034193582 get :end 2016-05-04 15:03:47.001
1431398495 set:begin 2016-05-04 15:03:48.005
1431398495 set:end 2016-05-04 15:03:53.007

Process finished with exit code 0

结果分析:写入数据的线程是在读取数据线程执行完毕之后才执行的,说明读取锁readLock对写入锁writeLock是排斥的,读操作的时候不允许其他线程进行写操作。

Run test3()  输出结果如下:

751872434 set:begin 2016-05-04 15:11:11.406
751872434 set:end 2016-05-04 15:11:16.409
1402691771 get :begin 2016-05-04 15:11:17.410
1402691771 get :end 2016-05-04 15:11:22.413

Process finished with exit code 0

结果分析:读取数据的线程是在写入数据线程执行完毕之后才执行的,说明写入锁writeLock对读取锁readLock是排斥的,写操作的时候不允许其他线程进行读操作。

Run test4()  输出结果如下:

926219290 set:begin 2016-05-04 15:14:53.647
926219290 set:end 2016-05-04 15:14:58.650
2112602078 set:begin 2016-05-04 15:14:59.654
2112602078 set:end 2016-05-04 15:15:04.657

Process finished with exit code 0

结果分析:第二个写入数据的线程是在第一个写入数据线程执行完毕之后才执行的,说明写入锁writeLock之间是互斥的,写操作的时候不允许其他线程进行写入操作。

总结:

使用读写锁 ReadWriteLock时,多个读操作是可以并发进行的,但是写操作对数据资源是独占的。

延伸:

在选取读写锁ReadWriteLoc还是同步锁synchronized的抉择中,我们可以通过两个因素来权衡:

1.读取数据的频率相对于写入数据的频率;

2.数据被写入锁定后,等待执行的线程数;

一般地,在需要并发控制的读写操作中,若读取数据频率较高且不是频繁的写入数据的时候,可以考虑使用读写锁以提高并发效率。

  • 参考文章:

1.ReadWriteLock用法

2.jdk-api-doc/ReadWriteLock.html

时间: 2024-08-14 08:07:08

【Java】读写锁 ReadWriteLock接口的相关文章

Java读-写锁

显示锁 在java5.0之前,在协调共享对象访问时可以使用的机制只有synchronized和volatile.java5.0增加了一种新的机制:ReentrantLock.ReentrantLock并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能.与内置锁不同的是Lock提供了一个无条件的.可轮询的.定时的以及可中断的锁获取操作,所有加锁和解锁都是显示的.在Lock的实现中必须提供与内部锁相同的内存可见性语义. /**Lock接口*/ public interface

读写锁ReadWriteLock

为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率. Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,详细的API可以查看JavaAPI文档. ReentrantReadWriteLock 和 ReentrantLock 不是继承关系,但都是基于 AbstractQueuedS

java读写锁实现数据同步访问

锁机制最大的改进之一就是ReadWriteLock接口和它的唯一实现类ReentrantReadWriteLock.这个类有两个锁,一个是读操作锁,另一个是写操作锁.使用读操作锁时可以允许多个线程同时访问,但是使用写操作锁时只允许一个线程进行.在一个线程执行写操作时,其他线程不能够执行读操作. 下面我们将通过范例学习如何使用ReadWriteLock接口编写程序.这个范例将使用ReadWriteLock接口控制对价格对象的访问,价格对象存储了两个产品的价格. 1. 创建一个价格信息类Prices

多线程之Lock锁和读写锁ReadWriteLock

JDK1.5之后有提供了另一种加锁方式Lock锁.Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作.此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition 对象. 锁是控制多个线程对共享资源进行访问的工具.通常,锁提供了对共享资源的独占访问.一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁.不过,某些锁可能允许对共享资源并发访问,如ReadWriteLock 的读取锁. synchronized 方法或语句的使用

JDK读写锁ReadWriteLock的升级和降级问题

JDK提供了写锁接口ReadWriteLock和它的实现ReentrantReadWriteLock.要实现一个读写锁,需要考虑很多细节,其中之一就是锁升级和锁降级的问题.什么是升级和降级呢?ReadWriteLock的javadoc有一段话: Can the write lock be downgraded to a read lock without allowing an intervening writer? Can a read lock be upgraded to a write

深入理解读写锁—ReadWriteLock源码分析

ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁.读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的. 所有读写锁的实现必须确保写操作对读操作的内存影响.换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容. 读写锁比互斥锁允许对于共享数据更大程度的并发.每次只能有一个写线程,但是同时可以有多个线程并发地读数据.ReadWriteLock适用于读多写少的并发情况. Java并发包中ReadWriteLock是一个接口,主要有两个方法,如下: public i

读写锁ReadWriteLock和缓存实例

读写锁:多个读锁不互斥,读锁与些锁互斥,写锁与写锁互斥.即:读的时候不允许写,写的时候不允许读,可以同时读. synchronized关键字和普通的Lock构造的锁,会造成读与读之间的互斥,因此读写锁可提高性能. 例子1:三个线程同时对一个共享数据进行读写. import java.util.Random; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantRea

Java读写锁,多线程环境下提升效率

读写锁 package cn.sniper.thread.lock; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class Cache {

Java读写锁(ReentrantReadWriteLock)学习

什么是读写锁 平时,我们常见的synchronized和Reentrantlock基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,哪怕是读操作.而读写锁是维护了一对锁(一个读锁和一个写锁),通过分离读锁和写锁,使得同一时刻可以允许多个读线程访问,但是在写线程进行访问时,所有的读线程和其他写线程均被阻塞. 读写锁的优点 1. 简化了读写交互场景编程的复杂度: 在常见的开发中,我们经常会定义一个共享的用作缓存的数据结构:比如一个大Map,缓存全部的城市Id和城市name对应关系.这个大Ma