Lock、ReentrantLock、synchronized

lock
https://www.cnblogs.com/dolphin0520/p/3923167.html

ReentrantLock
https://blog.csdn.net/qq_38293564/article/details/80515718

这个问题没看~~~
说说ReentrantLock是基于哪个类的?说说队列同步器

1、为什么会有lock的出现

synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢?

理由一: synchronized等待获取锁的线程一直等待,直到获取锁

代码块被synchronized修饰时,若获得锁的线程要等待IO或者其他原因(sleep)被阻塞了,那么其他线程只能等待该线程释放锁(方式1:正常运行完代码块,线程释放锁;方式2:发生异常,JVM让线程自动释放锁),这样会很影响程序执行效率。

Lock可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断)。

理由二: synchronized不能实现共享读,而lock能实现共享读

当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象

但是采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,那么线程在进行读操作时其他线程只能等待无法进行读操作。

而Lock可以使得多个线程都只是进行读操作时,线程之间不会发生冲突。

理由三: Lock可以知道线程有没有成功获取到锁

通过synchronized不知道线程有没有成功获取到锁,而Lock无法办到的。

2、ReentrantLock和synchronized的区别

1)Lock是一个接口,而synchronized是Java中的关键(JVM)字,synchronized是内置的语言实现

2)释放锁的方式不同:

如果采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后或者程序出现异常时,系统会自动让线程释放对锁的占用;

如果采用Lock必须主动去释放锁并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生

3)获取不到锁的处理方式的不同:

tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回,在拿不到锁时不会一直等待

tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。

lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程

由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中,或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为本身在前面的文章中讲过单独调用interrupt()方法不能中断正在运行过程中的线程只能中断阻塞过程中的线程

而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的只有一直等待下去。synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。

4)唤醒等待线程的方式不同

ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒,需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程

5)读共享,写不共享的实现

采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,所以当一个线程在进行读操作时其他线程只能等待无法进行读操作。通过Lock就可以使得多个线程进行读操作时,线程之间不会发生冲突

6)性能的不同

在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化借鉴了ReenTrantLock中的CAS技术都是试图在 用户态 就把加锁问题解决,避免进入内核态 的线程阻塞

7)可重入性

ReenTrantLock和synchronized都是是可重入的。两者都是同一个线程每进入一次锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁

8)公平锁和非公平锁

ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁

3、Lock的几种实现类

3.1 ReentrantLock

ReentrantLock(可重入锁)实现了Lock接口的类,并且ReentrantLock提供了更多的方法。下面通过一些实例看具体看一下如何使用ReentrantLock。

ReentrantLock是一个可重入且独占式的锁,它具有与使用synchronized监视器锁相同的基本行为和语义,但与synchronized关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。ReentrantLock,顾名思义,它是支持可重入锁的锁,是一种递归无阻塞的同步机制。除此之外,该锁还支持获取锁时的公平和非公平选择。

所谓的公平与非公平指的是在请求先后顺序上,先对锁进行请求的就一定先获取到锁,那么这就是公平锁,反之,如果对于锁的获取并没有时间上的先后顺序,如后请求的线程可能先获取到锁,这就是非公平锁。一般而言非,非公平锁机制效率往往会胜过 公平锁的机制,但在某些场景下,可能更注重时间先后顺序,那么公平锁自然是很好的选择

实现重进入

重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞,该特性的首先需要解决以下两个问题:

  • 线程再次获取锁:锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次获取成功;

  • 锁的最终释放:线程重复n次获取了锁,随后在第n次释放该锁后,其它线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前线程被重复获取的次数,而被释放时,计数自减,当计数为0时表示锁已经成功释放

ReentrantLock是通过自定义同步器实现锁的获取与释放,我们以非公平锁(默认)实现为例,对锁的获取和释放进行详解。

3.2 ReadWriteLock

ReadWriteLock也是一个接口,在它里面只定义了两个方法

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading.
     */
    Lock readLock();
 
    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing.
     */
    Lock writeLock();
}

一个用来获取读锁一个用来获取写锁。也就是说将文件的读写操作分开分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。下面的ReentrantReadWriteLock实现了ReadWriteLock接口

3.3 ReentrantReadWriteLock

ReentrantReadWriteLock里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和writeLock()用来获取读锁和写锁

如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程一直等待释放读锁

如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁

4、ReenTrantLock实现的原理

基于队列同步器AbstractQueueSychronier :队列同步器维护一个同步队列(双向链表),同时也维护一个同步状态state;它的方法分类:独占式锁和分享式锁

独占式锁的实现:加入同步队列时,结点需要自旋查看它的前一个结点是否获取锁如果获取的话它应该尝试获取锁(tryAcquire)。

详情请见:
https://blog.csdn.net/csdnlijingran/article/details/83057110

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值