为什么Java有了synchronized之后还造了Lock锁这个轮子?
众所周知,synchronized和Lock锁是java并发变成中两大利器,可以用来解决线程安全的问题。但是为什么Java有了synchronized之后还是提供了Lock接口这个api,难道仅仅只是重复造了轮子这么简单么?本文就来探讨一下这个问题。
谈到这个问题,其实很多同学第一反应都会说,Lock锁的性能比synchronized好,synchronized属于重量级的锁。但是在JDK 1.6版本之后,JDK对synchronized进行了一系列性能的优化,synchronized的性能其实有了大大的提升(如果不清楚的同学可以看一下 synchronized真的很重么?这篇文章,文章内详细的说明JDK对synchronized做了哪些优化),那么既然性能不是问题,那么主要的问题是什么呢?
synchronized抢占锁的特性
我们先来看一下synchronized抢占锁的特性。synchronized在抢占锁的时候,如果抢占不到,线程直接就进入阻塞状态了,而线程进入阻塞状态,其实什么也干不了,也释放不了线程已经占有的资源,并且也无法主动或者被动打断阻塞获取锁的操作,只有等别的线程释放锁之后才会被唤醒来重新获取锁。
synchronized阻塞获取锁产生的问题
那synchronized这种获取锁阻塞的特性,有什么问题么?其实有一种很重要的问题,那就是会产生死锁的问题。
那什么是死锁?死锁是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待的现象。
举个例子来说,线程1先对加A加锁,线程2对B加锁。代码运行到某一时刻,线程1需要对B加锁,但是此时B的锁已经被线程2占有,于是线程1就会阻塞,与此同时线程2同时也需要对A加锁,发现A已经被线程1持有,也会进入阻塞,于是线程1和线程2都在等对方释放资源,就产生了死锁的问题,并且由于synchronized阻塞的特性,线程无法主动或者被动停止阻塞,势必会导致这个死锁永远无法通过主动或者人为干预(其它线程干预)来解决。
那么有什么好的办法来解决阻塞导致死锁的问题呢?
我们分析一下死锁产生的问题主要是线程都在相互等待其它线程释放资源导致的,基于这个问题我们思考一下,如果一个线程获取不到锁,然后就停止获取锁,不阻塞,或者是阻塞一会就不再阻塞,又或是阻塞过程中被其他线程打断,那样这是不是就不是产生死锁的问题了。
就拿上面的例子来说,假设线程1获取B的阻塞锁超过一定时间,主动放弃获取B的锁,那么线程1代码就可以继续往下执行,当执行完之后,线程1释放了A锁,此时线程2就能获取到A的锁,那么线程2就可以继续执行了,这样是不是死锁的问题就完美解决了。
其实Lock锁就提供了上述提到的几种解决方案的api,接下来我们就来看看Lock锁提供的api。
Lock锁
void lockInterruptibly() throws InterruptedException;
阻塞可以被打断的加锁方法,这是一个被动放弃获取锁的方法。就是说其它线程主动当调用阻塞线程的interrupt方法之后,该阻塞线程就会放弃继续获取锁,然后抛出InterruptedException 异常,所以对于使用方来说,只要捕获这个异常,就能保证线程的代码继续执行了。
boolean tryLock();
这个方法是尝试加锁,加锁失败后就放弃加锁,不会阻塞,直接返回false。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
这个方法相比上面的就是尝试加锁失败后在阻塞的一定时间之后,如果还没有获取到锁,那么就放弃获取锁。
Lock接口的实现有很多,但基本上都是基于Java的AQS的实现来完成的。AQS其实主要是维护了一个锁的状态字段state和一个双向链表。当线程获取锁失败之后,就会加入到双向链表中,然后阻塞或者不阻塞,这得看具体的方法实现。
Lock接口的一个实现ReentrantLock就是基于AQS实现来讲的,这里就不继续展开讲解ReentrantLock的实现原理,如果有感兴趣的同学,可以看一下 一文带你看懂Java中的Lock锁底层AQS到底是如何实现的 这篇文章,文章是基于ReentrantLock来讲解AQS的加锁和释放锁的原理。
总结
好了,到这里其实大家应该知道了,为什么需要Lock锁,因为synchronized获取不到锁的时候会阻塞,并且阻塞不可被打断的特性会导致可能会产生死锁的问题,为了解决这个问题,Java就提供了Lock锁的实现,从主动放弃获取锁或者被动放弃获取锁的方式,解决一直阻塞可能产生的死锁问题。
如果觉得这篇文章对你有所帮助,还请帮忙点赞、在看、转发一下,码字不易,非常感谢!
如果你想联系我,欢迎关注我的个人的微信公众号三友的java日记,每天都会发布技术性的文章,期待与你一起进步。
为什么Java有了synchronized之后还造了Lock锁这个轮子?的更多相关文章
- Java有了synchronized,为什么还要提供Lock
摘要:在Java中提供了synchronized关键字来保证只有一个线程能够访问同步代码块.既然已经提供了synchronized关键字,那为何在Java的SDK包中,还会提供Lock接口呢? 本文分 ...
- 【Java并发编程】:并发新特性—Lock锁和条件变量
简单使用Lock锁 Java5中引入了新的锁机制——Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接 ...
- (删)Java线程同步实现二:Lock锁和Condition
在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...
- Java中的Lock锁
Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...
- Lock、synchronized和ReadWriteLock,StampedLock戳锁的区别和联系以及Condition
https://www.cnblogs.com/RunForLove/p/5543545.html 先来看一段代码,实现如下打印效果: 1 2 A 3 4 B 5 6 C 7 8 D 9 10 E 1 ...
- Java并发--lock锁详解
在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方 ...
- java并发lock锁详解和使用
一.synchronized的缺陷 synchronized是java中的一个关键字,也就是说是Java语言内置的特性.那么为什么会出现Lock呢? 在上面一篇文章中,我们了解到如果一个代码块被syn ...
- java中同步(synchronized)详解
一.开山篇: 1.synchronized的使用 一个程序中,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题.在Java中内置了语言级的同步原语--synchronized,这也大大简 ...
- 【高并发】面试官:Java中提供了synchronized,为什么还要提供Lock呢?
写在前面 在Java中提供了synchronized关键字来保证只有一个线程能够访问同步代码块.既然已经提供了synchronized关键字,那为何在Java的SDK包中,还会提供Lock接口呢?这是 ...
随机推荐
- 可想实现一个自己的简单jQuery库?(九)
Lesson-8 事件机制 在讲事件机制之前呢,我们有一个很重要的东西要先讲,那就是如何实现事件委托(代理). 只有必须先明白了如何实现一个事件委托,我们才能更好的去实现on和off.在我看来,on和 ...
- canvas 1px 出现模糊解决方法及原理
关于canvas绘制1像素出现模糊的原因及解决方法 canvas是html5中非常强大的功能,但是在绘制的时候如果出现1像素,例如画一条1像素的线可能出现模糊情况. 一.解决方法 网上比较常见的解决方 ...
- 用CSS实现Tab页切换效果
用CSS实现Tab切换效果 最近切一个页面的时候涉及到了一个tab切换的部分,因为不想用js想着能不能用纯CSS的选择器来实现切换效果.搜了一下大致有下面三种写法. 利用:hover选择器 缺点:只有 ...
- Element UI table参数中的selectable的使用
Element UI table参数中的selectable的使用中遇到的坑:页面: <el-table-column :selectable='selectable' type="s ...
- ES6-11学习笔记--let
新声明方式:let 1.不属于顶层对象 window 2.不允许重复声明 3.不存在变量提升 4.暂时性死区 5.块级作用域 原来var声明: var a = 5; console.log(a); ...
- Spark周总结(一)
本周学习内容: 1.搭建虚拟机Spark环境 2.idea编写Scala脚本并在yarn上运行 总结: 这周是回家第一周,虽然没啥事,但是还是想放松放松,也是万事开头难,跟着教程做,但总有几步跟教程上 ...
- java中downcast向下转型到底有什么用
What is the point of downcast? 当一个方法只有子类才有,马克-to-win:不是说基类和子类都有,开始时又是基类指针指向派生类,这时就需要downcast, see th ...
- Go xmas2020 学习笔记 07、Formatted & File I/O
07-Formatted & File I/O. I/O steams. formatted I/O. fmt functions. file I/O. Practice ① I/O. Alw ...
- go - 内存分配机制详解
一般程序的内存分配,从高位到低位依次为 全局静态区:用于存储全局变量.静态变量等:这部分内存在程序编译时已经分配好,由操作系统管理,速度快,不易出错. 栈:函数中的基础类型的局部变量:由程序进行系统调 ...
- 查找SQL SERVER卡顿语句
SELECT [session_id], [blocking_session_id] AS '正在阻塞其他会话的会话ID', DB_NAME([database_id]) AS '数据库名称', [r ...