为什么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接口呢?这是 ...
随机推荐
- JavaScript 遍历对象、数组总结
在日常工作过程中,我们对于javaScript遍历对象.数组的操作是十分的频繁的,今天抽空把经常用到的方法小结一下,方便今后参考使用! javaScript遍历对象总结 1.使用Objec ...
- Spark入门之idea编写Scala脚本
一.安装Scala插件 1.File->Settings 2.Plugins->Msrketplace->搜索Scala并安装 (或者自己下载合适的scala版本,教程:自己给ide ...
- 小程序拿checkbox的checked属性
方法一.checkbox <checkbox class="round red" bindtap="checkboxChange" checked=&q ...
- 深入理解nodejs的异步IO与事件模块机制
node为什么要使用异步I/O 异步I/O的技术方案:轮询技术 node的异步I/O nodejs事件环 一.node为什么要使用异步I/O 异步最先诞生于操作系统的底层,在底层系统中,异步通过信号量 ...
- JAVASE for 笔记
//0到100中奇数偶数的和package com.huang.boke.flowPath;public class Fordeme { public static void main(String[ ...
- Linux利用crontab创建计划任务详解
crontab 周期性计划任务 cron是Linux下的定时执行工具,可以在无需人工干预的情况下运行作业. 当需要周期性地重复执行任务时可以使用cron服务:该服务每分钟检查一次,并执行符合条件的任务 ...
- ADO访问Excel
需要安装驱动:Microsoft Access Database Engine,可搜索下载,有64位和32位之分. 随便新建一个后缀名为udl的文件,双击打开.注意,现如今一般都是64位系统,双击打开 ...
- Java-NIO之Channel(通道)
1:Channel是什么 通道表示与实体的开放连接,例如硬件设备.文件.网络套接字或能够执行一个或多个不同 I/O 操作(例如读取或写入)的程序组件. 1.1:Channel与Stream的对比 St ...
- Py点亮
- partTwo自动出题程序第三阶段
课堂测试3: 2.可定制(数量/打印方式):输入大的数量值,测试一下系统是否崩溃,反向查找系统是否优化的余地: 3.定制操作数的个数: 4.定制是否有乘除法 5.定制是否有括号(随机加入) 6 .定制 ...