课程名称:玩转Java并发工具,精通JUC,成为并发多面手
课程章节:第5章 不可不说的“锁”事【种类繁多,如何一一突破?】
课程讲师: 悟空
课程内容
1.1、简介、地位、作用
- 锁是一种工具,用于控制对共享资源的访问。
- Lock和synchronized,这两个是最常见的锁,它们都可以达到线程安全的目的,但是在使用上和功能上又有较大的不同。
- Lock并不是用来代替synchronized的,而是当使用synchronized不合适或不足以满足需求的时候,来提供高级功能的。
- Lock接口中最常见的实现类就是ReentrantLock
- 通常情况下,Lock只允许一个线程来访问这个共享资源。不过有的时候,一些特殊的实现也可允许并发访问,比如ReadWriteLock里面的ReadLock
1.2、为什么synchronized不够用?为什么需要Lock?
1.2.1、为什么synchronized不够用?
- 效率低:锁的释放情况少(1.把锁的代码执行完毕;2.执行到一半发生异常了,JVM释放;除了wait方法能释放锁外,是没有办法释放锁的)、试图获得锁时不能设定超时、不能中断一个正在试图获得锁的线程
- 不够灵活(读写锁更灵活):加锁和释放锁的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的
- 无法知道是否成功获取到锁
1.3、方法介绍
- 在Lock中声明了四个方法来获取锁
- lock()、tryLock()、tryLock(long time,TimeUtil unit)和lockInterruptibly()
1.3.1、lock()
- lock()就是最普通的获取锁。如果锁已被其他线程获取,则进行等待
- Lock不会像synchronized一样在异常时自动释放锁
- 因此最佳实践是,在finally中释放锁,以保证异常发生时锁一定被释放
- lock()方法不能被中断,这会带来很大的隐患:一旦陷入死锁,lock()就陷入永久等待
1.3.2、tryLock()
- tryLock()用来尝试获取锁,如果当前锁没有被其他线程占用,则获取成功,返回true,否则返回false,代表获取锁失败。
- 相比lock,这样的方法显然功能更强大了,我们可以根据是否获取到锁来决定后续程序的行为
- 该方法会立即返回,即使再拿不到锁时也不会一直在那等
1.3.2、tryLock(long time,TimeUnit unit)
- 超时就放弃
/**
* 描述:用tryLock来避免死锁
*/
public class TryLockDeadLock implements Runnable {
int flag = 1;
static Lock lock1 = new ReentrantLock();
static Lock lock2 = new ReentrantLock();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (flag == 1) {
try {
if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程1获取到了锁1");
Thread.sleep(new Random().nextInt(1000));
if (lock2.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程1获取到了锁2");
System.out.println("线程1成功获取到了两把锁");
break;
} finally {
lock2.unlock();
}
} else {
System.out.println("线程1获取锁2失败,已重试");
}
} finally {
lock1.unlock();
Thread.sleep(new Random().nextInt(1000));
}
} else {
System.out.println("线程1获取锁1失败,已重试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (flag == 0) {
try {
if (lock2.tryLock(3000, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程2获取到了锁2");
Thread.sleep(new Random().nextInt(1000));
if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程2获取到了锁1");
System.out.println("线程2成功获取到了两把锁");
break;
} finally {
lock1.unlock();
}
} else {
System.out.println("线程2获取锁1失败,已重试");
}
} finally {
lock2.unlock();
Thread.sleep(new Random().nextInt(1000));
}
} else {
System.out.println("线程2获取锁2失败,已重试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
TryLockDeadLock tryLockDeadLock1 = new TryLockDeadLock();
TryLockDeadLock tryLockDeadLock2 = new TryLockDeadLock();
tryLockDeadLock1.flag = 1;
tryLockDeadLock2.flag = 0;
new Thread(tryLockDeadLock1).start();
new Thread(tryLockDeadLock2).start();
}
}
1.3.3、lockInterruptibly()
- 相当于tryLock(long time,TimeUnit unit)把超时时间设置为无限。在等待锁的过程中,线程可以被中断
1.3.4 unlock
- 解锁
- 应该被写在finally里面,然后再去写业务逻辑。不写就会导致死锁
课程收获
今天学习了锁的相关知识:
点击查看更多内容
1人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦