lock.ReadWriteLock使用方法

本文介绍了Java中的Lock接口及其实现,对比了传统的synchronized关键字,详细讲解了ReadWriterLock的使用,包括其并发访问优势和策略决策。通过一个示例展示了如何在多线程环境中使用ReentrantReadWriteLock进行读写操作。
摘要由CSDN通过智能技术生成

Lock锁

锁实现提供了比使用synchronized方法和语句更广泛的锁操作。它们允许更灵活的结构,可能有不同的属性,可能支持多个关联的Condition对象。

锁是用于控制多个线程对共享资源的访问的工具。

通常,锁提供对共享资源的独占访问:一次只有一个线程可以获得锁,并且所有访问共享资源的访问都需要先获得锁。但是,有些锁可能允许并发访问共享资源。例如readwritellock的读锁。

同步方法或语句的使用提供了对每个对象关联的隐式监视器锁的访问,但强制所有锁的获取和释放都以块结构的方式进行:当多个锁被获取时,它们必须以相反的顺序被释放,并且所有的锁都必须在获取锁的同一个词法作用域中被释放。虽然同步方法和语句的作用域机制使使用监视器锁进行编程变得更加容易,并有助于避免许多涉及锁的常见编程错误,但在某些情况下,您需要以更灵活的方式使用锁。

例如,一些用于遍历并发访问的数据结构的算法需要使用“手拉手”或“链锁”:您获取节点A的锁,然后是节点B,然后释放A并获取C,然后释放B并获取D,等等。

Lock接口的实现允许在不同范围内获取和释放锁,并允许以任何顺序获取和释放多个锁,从而允许使用这种技术。

传统synchronized
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

lock.ReadWriteLock();

ReadlriteLock维护一对相关联的锁,一个用于只读操作,一个用于写操作。只要没有写线程,读锁可以被多个读线程同时持有。写锁是排他的。

所有的readwritellock实现必须保证writellock操作的内存同步效果(在Lock接口中指定)也与相关的readLock保持一致。也就是说,成功获取读锁的线程将看到之前发布的写锁的所有更新。

与互斥锁相比,读写锁在访问共享数据时允许更高级别的并发。它利用了一个事实,即虽然一次只有一个线程(写线程)可以修改共享数据,但在许多情况下,任意数量的线程都可以并发地读取数据(因此读取线程)。理论上,使用读写锁所允许的并发性的增加将导致性能的提高,而不是使用互斥锁。在实践中,只有在共享数据的访问模式合适的情况下,这种并发性的增加才能在多处理器上完全实现。

读写锁是否会提高性能的使用互斥锁的频率取决于读取数据被修改相比,读和写操作的持续时间,和争用数据——也就是说,线程的数量,将尝试读或写数据在同一时间。例如,最初用数据填充的集合,在频繁搜索的情况下(例如某种目录)很少修改的集合是使用读写锁的理想候选对象。然而,如果更新变得频繁,那么数据的大部分时间都被独占锁定,并且几乎不会增加并发性。此外,如果读操作太短,读写锁实现的开销(其本质上比互斥锁复杂)可能会主导执行成本,特别是当许多读写锁实现仍然通过一小段代码序列化所有线程时。最终,只有分析和测量才能确定读写锁的使用是否适合您的应用程序

虽然读写锁的基本操作很简单,但实现必须做出许多策略决策,这些决策可能会影响给定应用程序中读写锁的有效性。

这些策略的例子包括:

  • 当读锁和写锁都在等待时,当写锁释放时,决定是授予读锁还是写锁。作者的偏好是普遍的,因为写作被认为是短的和不频繁的。读者偏好不太常见,因为如果读者像预期的那样频繁且长期存在,就会导致写操作的长时间延迟。公平或“有序”的实现也是可能的。

  • 当一个读取器处于活动状态,而一个写入器正在等待时,确定请求读锁的读取器是否被授予读锁。偏爱读器会无限期地延迟写器,而偏爱写器会降低并发的可能性。确定锁是否可重入:具有写锁的线程可以重新获得它吗?它能在持有写锁的同时获得读锁吗?读锁本身是可重入的吗?

  • 写锁可以降级为读锁而不允许写入者介入?读锁是否可以优先升级为写锁,而不是其他等待的读锁或写锁?

在评估给定实现对应用程序的适用性时,应该考虑所有这些因素。

ReadWriteLockTest
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class ReadWriteLockTest {

    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();
        //写入操作
        for (int i = 0; i < 6; i++) {
            int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }
        //读取操作
        for (int i = 0; i < 6; i++) {
            int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }
}

class MyCacheLock {
    private final Map<String, Object> map = new HashMap<>();
    //读写锁
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    // 存,写入的时候只有一个人操作
    public void get(String key) {
        lock.readLock().lock();
        Object o = null;
        try {
            System.out.println(Thread.currentThread().getName() + "读取");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取ok" + o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
    }

    public void put(String key, Object value) {
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入完毕");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }
}

执行结果, 【写入和读取是多发线程,结果打印顺序并不是唯一的,主要在于CPU时间切片选择】

1写入1
1写入完毕
2写入2
2写入完毕
0写入0
0写入完毕
4写入4
4写入完毕
5写入5
5写入完毕
3写入3
3写入完毕
1读取
2读取
0读取
4读取
5读取
3读取
0读取ok0
1读取ok1
4读取ok4
2读取ok2
5读取ok5
3读取ok3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ReadWriteLockJava中提供的一种读写锁机制,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。ReadWriteLock中包含一个读锁和一个写锁,读锁可以被多个线程同时持有,但写锁只能被一个线程持有。 下面是ReadWriteLock使用示例: ```java import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockDemo { private static final ReadWriteLock lock = new ReentrantReadWriteLock(); private static int value = 0; public static void main(String[] args) { // 创建10个读线程和1个写线程 for (int i = 0; i < 10; i++) { new Thread(new ReadTask()).start(); } new Thread(new WriteTask()).start(); } // 读线程 static class ReadTask implements Runnable { public void run() { lock.readLock().lock(); // 获取读锁 try { System.out.println(Thread.currentThread().getName() + " read " + value); } finally { lock.readLock().unlock(); // 释放读锁 } } } // 写线程 static class WriteTask implements Runnable { public void run() { lock.writeLock().lock(); // 获取写锁 try { value++; // 修改共享变量 System.out.println(Thread.currentThread().getName() + " write " + value); } finally { lock.writeLock().unlock(); // 释放写锁 } } } } ``` 在上面的示例中,我们定义了一个ReadWriteLock对象,并创建了10个读线程和1个写线程。读线程通过调用readLock()方法获取读锁,写线程通过调用writeLock()方法获取写锁,并在访问共享变量后释放锁。由于读锁允许多个线程同时持有,所以读线程可以同时执行,而写线程必须等到所有读线程执行完毕后才能执行。 需要注意的是,由于ReadWriteLock是基于Lock接口实现的,因此在使用ReadWriteLock时需要注意锁的获取和释放顺序,避免出现死锁或者锁竞争等问题。同时,在使用ReadWriteLock时也需要注意共享变量的可见性和线程安全性,避免出现数据不一致等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值