Synchronize 和 Lock (ReentrantLock)的区别

目录

Lock存在的意义

情景一:

情景二:

Lock的应用编程: 

ReentrantLock 的应用

 ReentrantReadWriteLock 的应用

synchronized 和 Lock 的比较

锁的相关概念介绍:

1.可重入锁

2.可中断锁

3.公平锁

4.读写锁


Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类(java.util.concurrent(并发的).locks包),通过这个类可以实现同步访问;

Lock存在的意义

Lock是synchronized的升级:

情景一:

一个代码块被synchronized修饰了,当一个线程获取了对应的锁,在其没执行完前,其他线程只能等待;若占有锁的线程因为等待IO或者其他原因(比如调用sleep方法)被阻塞了,一直不释放锁,那么其他线程搞不好会无限等待;

破局:Lock可以通过手动调用代码实现:只等待一定的时间 tryLock() 或者能够响应中断lockInterruptibly();

注意:synchronized 阻塞不会释放锁,但因为异常会释放锁;

Lock是一个接口 ,ReentrantLock是唯一实现了Lock接口的类,是一个可重入独占式的锁;ReentrantLock 里面有一个内部类 SyncSync 继承AQS(AbstractQueuedSynchronizer);ReentrantLock 的底层就是由 AQS 来实现的;

package java.util.concurrent.locks;

public interface Lock {

    /**
     * 用来获取锁。如果锁已被其他线程获取,则进行等待
     */
    void lock();

    /**
     * 当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够中断线程的等待状态
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * 用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回 false;
     * 也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
     */
    boolean tryLock();

    /**
     * 在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false;
     * 此方法来确保线程只等待一定的时间,如果它没有获得对象的锁定,它只是记录和退出;
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

  
    void unlock();

    Condition newCondition();

}

情景二:

采用synchronized实现同步,就会导致一个问题:如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程读操作只能等待无法进行读操作。

破局:Lock可以通过 ReentrantReadWriteLock 的 readLock() 来实现;

对于写操作的需求:一个线程想去写共享资源,就不应该允许其他线程对该资源进行读和写的操作;这个synchronized本身就能满足,Lock在这方面没啥特别的改进???

ReadWriteLock也是一个接口,ReentrantReadWriteLock实现了ReadWriteLock接口;

  • ReadWriteLock:线程1读完,线程2才能读;
  • ReentrantReadWriteLock: 多个线程可以同时进行读操作;

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

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

package java.util.concurrent(并发的).locks;


public interface ReadWriteLock {
    /**
     * 用来获取读锁
     */
    Lock readLock();
 
    /**
     * 用来获取写锁
     *
     */
    Lock writeLock();
}

ReentrantReadWriteLock 在实际项目中使用的并不多,面试中也问的比较少,简单了解即可。JDK 1.8 引入了性能更好的读写锁 StampedLock

​​​​​​​

Lock的应用编程: 

  

ReentrantLock 的应用

 ReentrantReadWriteLock 的应用

 ReentrantLock 比 synchronized增加了一些高级功能

相比synchronizedReentrantLock增加了一些高级功能。主要来说主要有三点:

  • 等待可中断 : ReentrantLock提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly() 来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
  • 可实现公平锁 : ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。
  • 可实现选择性通知(锁可以绑定多个条件)synchronized关键字与wait()notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition()方法。

synchronized 和 Lock 的比较

  1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
  2. synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
  3. Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
  4. 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
  5. Lock可以提高多个线程进行读操作的效率。

锁的相关概念介绍:

1.可重入锁

可重入锁:指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。

synchronized 和   ReentrantLock 都是可重入锁。

可重入锁的意义之一在于防止死锁

实现原理:通过为每个锁关联一个请求计数器和一个占有它的线程。当计数为0时,认为锁是未被占有的;线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数器置为1 ;如果同一个线程再次请求这个锁,计数器将递增;每次占用线程退出同步块,计数器值将递减。直到计数器为0, 锁被释放。

2.可中断锁

可中断锁:顾名思义,就是可以相应中断的锁。

在Java中,synchronized就不是可中断锁,而Lock是可中断锁。

如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。

3.公平锁

公平锁:即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。

非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁,产生饥饿;

4.读写锁

读写锁:将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。

正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。

ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。

可以通过readLock()获取读锁,通过writeLock()获取写锁。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值