【java并发编程】lock接口

一、简介

java中的lock接口,功能与synchronizied关键字类似。用来处理同步操作。

jdk1.5之前使用synchronizied关键字完成同步作用。

jdk1.5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁功能,只需在使用时显式地获取和释放锁。虽然它缺少了synchronized隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized关键字所不具备的同步特性。

相比synchronized关键字lock好处:
1、增加超时时间设置,避免synchronized代码块中执行时间过长
2、提供tryLock()方法,可以尝试获取锁,如果获取不到可以转去做别的事情。
3、提供condition条件
2、可以满足一些复杂的获取锁的场景。如先获得锁A,然后再获取锁B,当锁B获得后,释放锁A同时获取锁C,当锁C获得后,再释放B同时获取锁D

二、lock接口继承关系

lock接口的6个方法

// 获取锁
void lock()   
// 如果当前线程未被中断,则获取锁,可以响应中断
void lockInterruptibly()   
// 返回绑定到此 Lock 实例的新 Condition 实例
Condition newCondition()   
// 仅在调⽤时锁为空闲状态才获取该锁,可以响应中断
boolean tryLock()   
// 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁
boolean tryLock(long time, TimeUnit unit)   
// 释放锁
void unlock()

  lock接口的继承关系看,主要有三个锁ReentrantLock可重入锁(常用)、ReentrantReadWriteLock可重入读写锁(子类实现lock接口)、StampedLock

三、lock实现类

1、ReentrantLock可重入锁(常用)

ReentrantLock有公平锁和非公平锁(默认)之分

Condition对象

synchronized可以配合wait和notify实现线程在条件不满足时等待,条件满足时唤醒,
ReentrantLock使用Condition对象实现wait和notify的功能
使用Condition时,引用的Condition对象必须从Lock实例的newCondition返回。

class TaskQueue {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private Queue<String> queue = new LinkedList<>();

    public void addTask(String s) {
        lock.lock();
        try {
            queue.add(s);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public String getTask() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                condition.await();
            }
            return queue.remove();
        } finally {
            lock.unlock();
        }
    }
}

await()释放当前锁,进入等待状态
signal()会唤醒某个等待的线程
signalAll()会唤醒所有等待的线程

加锁、释放锁原理

AQS参考:【java并发编程】AQS框架_现实、太残忍的博客-CSDN博客

基本就等于aqs的原理(继承了AbstractQueuedSynchronizer,只有tryAcquire方法和tryRelease方法是子类实现的

使用方法

Lock lock = new ReentrantLock();
try {
	lock.lock();
	//业务代码
} finally {
	lock.unlock();
}

2、ReentrantReadWriteLock可重入读写锁

多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行。
但是,如果有一个线程想去写共享资料,就不应该再有其他线程可以对该资源进行读或写
小总结:
  读-读 能共存
  读-写 不能共存
  写-写 不能共存

使用方法

        //读写锁
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        //读锁
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        //写锁
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        
        //写锁
        try {
            writeLock.lock();
            //业务代码
        } finally {
            writeLock.unlock();
        }

        //读锁
        try {
            readLock.lock();
            //业务代码
        } finally {
            readLock.unlock();
        }

3、StampedLock读写锁

StampedLock是java8新引入的锁比ReentrantReadWriteLock更快的一种锁,支持乐观读、悲观读锁和写锁。

它是读写锁的实现,对比 ReentrantReadWriteLock 主要不同是该锁不允许重入,多了乐观读的功能,使用上会更加复杂一些,但是具有更好的性能表现。

StampedLock 的状态由版本和读写锁持有计数组成。 获取锁方法返回一个邮戳,表示和控制与锁状态相关的访问; 这些方法的“尝试”版本可能会返回特殊值 0 来表示获取锁失败。

三种模式如下:

  • 写锁writeLock,是个排它锁或者叫独占锁,同时只有一个线程可以获取该锁,当一个线程获取该锁后,其它请求的线程必须等待,当目前没有线程持有读锁或者写锁的时候才可以获取到该锁,请求该锁成功后会返回一个stamp票据变量用来表示该锁的版本,当释放该锁时候需要unlockWrite并传递参数stamp。
  • 悲观读锁readLock,是个共享锁,在没有线程获取独占写锁的情况下,同时多个线程可以获取该锁,如果已经有线程持有写锁,其他线程请求获取该读锁会被阻塞。这里讲的悲观其实是参考数据库中的乐观悲观锁的,这里说的悲观是说在具体操作数据前悲观的认为其他线程可能要对自己操作的数据进行修改,所以需要先对数据加锁,这是在读少写多的情况下的一种考虑,请求该锁成功后会返回一个stamp票据变量用来表示该锁的版本,当释放该锁时候需要unlockRead并传递参数stamp。
  • 乐观读锁tryOptimisticRead,是相对于悲观锁来说的,在操作数据前并没有通过CAS设置锁的状态,如果当前没有线程持有写锁,则简单的返回一个非0的stamp版本信息,获取该stamp后在具体操作数据前还需要调用validate验证下该stamp是否已经不可用,也就是看当调用tryOptimisticRead返回stamp后到到当前时间间是否有其他线程持有了写锁,如果是那么validate会返回0,否者就可以使用该stamp版本的锁对数据进行操作。由于tryOptimisticRead并没有使用CAS设置锁状态所以不需要显示的释放该锁。该锁的一个特点是适用于读多写少的场景,因为获取读锁只是使用与或操作进行检验,不涉及CAS操作,所以效率会高很多,但是同时由于没有使用真正的锁,在保证数据一致性上需要拷贝一份要操作的变量到方法栈,并且在操作数据时候可能其他写线程已经修改了数据,而我们操作的是方法栈里面的数据,也就是一个快照,所以最多返回的不是最新的数据,但是一致性还是得到保障的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值