对 Synchronized 和 Lock 的简单理解

概念

Synchronized

Synchronized 是Java 并发编程中很重要的关键字,另外一个很重要的是 volatile。Syncronized 的目的是一次只允许一个线程进入由他修饰的代码段,从而允许他们进行自我保护。进入由 Synchronized 保护的代码区首先需要获取 Synchronized 这把锁,其他线程想要进入相同代码区执行必须等待 Synchronized 锁被释放掉,然后再争抢 Synchronized 锁,下一个抢到锁的线程才能进入相同代码区执行。

Synchronized 锁住的代码区域执行完成后会自动把锁归还,即释放锁。

Synchronized 是可重入、不可中断,非公平锁。

Lock

Lock 是 Java 并发编程中很重要的一个接口,需要手动加锁和手动解锁,一般会成对儿出现 lock.lock() 、lock.unlock()两个方法来进行加锁和解锁。 下面再介绍两个实现了 Lock 接口的锁。

ReetrantLock

可重入锁,内部定义了公平锁、非公平锁。

ReadWriteLock

内部分为读锁、写锁。将读操作和写操作进行分离,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。
这里多提一点儿 ReentrantReadWirteLock 实际上是实现了ReadWirteLock 接口,并非 Lock 接口。

使用

Synchronized

方法

在方法申明上使用 Synchronized,使进入该方法的线程同一时刻只允许进入一个,其他线程只能排队等候。

private int counter;
public synchronized void count() {
    counter++;
}
代码段

在某段儿代码上使用Synchronized,使进入该段儿代码的线程同一时刻只允许进入一个,其他线程只能排队等候。

private int counter;
public synchronized void count(Object obj) {
    synchronized (obj) {
        counter++;
    }
}
对象

对当前对象进行加锁。

private int counter;
public synchronized void count() {
    synchronized (this) {
        counter++;
    }
}

Lock

Lock是一个接口,主要方法如下:

public interface Lock {
    void lock();
    void unlock();
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void lockInterruptibly() throws InterruptedException;
    Condition newCondition();
}

下面将会简单地介绍一下每个方法:

  • lock()
    该方法是最常使用来获取锁的方法,这里需要注意的是 lock() 不会主动去释放锁,即使是发生了异常,也不会释放,所以需要通过显示的调用 unlock() 来释放锁。
Lock lock = ...;
lock.lock(); // 获取锁
try{
    // do something ...
} catch (Exception ex){
   // handle exception
} finally {
    lock.unlock(); // 释放锁
}
  • unlock()
    太好理解就不再赘述了。
  • trylock()
    该方法是有返回值的,使用该方法来尝试获取锁,如果获取成功,则返回true;如果获取失败(即锁已被其他线程获取),则返回false。这里需要重点说的是,该方法拿不到锁不会像lock()方法一样一直等待,而是会立即返回(返回false)。
  • trylock(long time, TimeUnit unit)
    该方法和tryLock()方法非常类似,区别在于这个方法在拿不到锁的时候,会等待一段时间,在时间期限之内如果还拿不到锁,才会返回 false;如果一开始就拿到锁或者在等待期间内拿到了锁,则返回 true。
  • lockInterruptibly()
    该方法略微有些不一样,当「线程A」通过该方法去获取锁的时候,如果「线程A」需要等待(也就是说其他线程已经抢到了锁),在此场景下,「线程A」可响应中断,即中断线程的等待状态,说的再直白点儿就是「线程A」可以通过调用 interrupt() 方法来中断一直处于等待的场景。

重点问题

我们可以通过 lockInterruptibly() 与 lock()、trylock()、trylock(long time, TimeUnit unit) 几个方法的不同得出一个结论:

当一个线程获取了锁之后,该线程是不会被 interrupt() 方法中断的。interrupt() 方法只可能作用于那些处于等待状态的线程,而又不是所有处于等待的线程都可以响应 interrupt,只有那些使 lockInterruptibly() 方法获取锁但没获取到而处于等待状态的线程才会响应 interrupt。而用 synchronized 修饰的,当一个线程处于等待某个锁的状态,是无法被中断的,只能一直等待下去。

区别

  • Syncronized 是Java 中的一个关键字,存在于 JVM 层面;Lock 是 Java 中的一个接口。
  • 在 Syncronized 中,假设线程 A 获得锁,B 线程等待。如果 A 发生阻塞,那么 B 会一直等待;在 Lock 中,会分情况而定,Lock 中有尝试获取锁的方法(trylock),如果尝试获取到锁,返回true,无需一直等待;反之,返回false,也不会一直等待。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cab5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值