JUC 源码解析:lock锁与synchronized锁的区别
本文使用 jdk1.8
Lock 锁的使用注意事项
- 要在 finally 块中释放锁。保障锁一定能被释放
- 不要把加锁代码写进 try 块里。因为我们可能会自己实现Lock接口,在一些实现中,如果获取锁时发生了异常,可能导致锁被无故释放
lock 与 synchronized 的区别
1. synchronized 拓展性和灵活性不如 Lock
-
synchronized 能隐式的获取锁释放锁,虽然方便,但lock纯手动显得更灵活。
-
Lock 接口可以自己写实现类,可以实现很多自定义的方法,synchronized与它相比非常呆板
2. jstack 线程状态不同
使用 lock 加锁,和使用 synchronized 加锁。没拿到锁的线程,状态是不同的。
-
线程如果没有竞争到 synchronized 锁,会呈现出
blocked
阻塞状态 -
线程如果没有竞争到 Lock 锁,会呈现出
waiting
等待状态
来用代码演示看看,先看看synchronized锁状态:
static SheepLock lock = new SheepLock();
public static void main(String[] args) throws InterruptedException {
new Thread(Main::trySheepLock, "A").start();
new Thread(Main::trySheepLock, "B").start();
}
public static void trySheepLock() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "获取到小羊锁");
while (true) {} // 无限循环,不释放锁
}
}
在终端中用这样的命令查看 jstack 日志:
在日志中找到这A、B这两个线程
可以看到:
- A抢到了锁,线程状态是
RUNNABLE
- B没有抢到锁,线程状态是
BLOCKED
,是阻塞
把这段代码改成 Lock 锁,然后用相同的方法看看锁状态(SheepLock
是我自定义的Lock实现,可以满足基本的锁需求)
static SheepLock lock = new SheepLock();
public static void main(String[] args) throws InterruptedException {
new Thread(Main::trySheepLock, "A").start();
new Thread(Main::trySheepLock, "B").start();
}
public static void trySheepLock() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取到小羊锁");
while (true) {} // 无限循环,不释放锁
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
可以看到:
- 线程A抢到了锁,状态是
RUNNABLE
- 线程B没有抢到锁,状态是
WATTING
,是等待
3. Lock 可以响应中断
Lock 接口中有这样的方法定义
void lockInterruptibly() throws InterruptedException;
与 synchronized 不同,Lock 的这个方法可以响应中断。
当拿到锁的线程被中断时,异常会抛出,锁会被释放
4. Lock 可以设置:在截止时间之前获取锁
Lock 接口中有这样的方法定义:
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
Lock 可以设置在截止时间之前获取锁,如果获取不到,会返回 false
5. Lock 可尝试非阻塞(非等待)获得锁
Lock 接口中有这样的方法定义:
boolean tryLock();
它可以尝试获取锁,只尝试一次,而不会让线程CAS自旋,失败一次就返回 false
标签:JUC,Thread,synchronized,lock,源码,线程,Lock,new From: https://www.cnblogs.com/yangruomao/p/18191755