众所周知,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锁这个轮子?的更多相关文章

  1. Java有了synchronized,为什么还要提供Lock

    摘要:在Java中提供了synchronized关键字来保证只有一个线程能够访问同步代码块.既然已经提供了synchronized关键字,那为何在Java的SDK包中,还会提供Lock接口呢? 本文分 ...

  2. 【Java并发编程】:并发新特性—Lock锁和条件变量

    简单使用Lock锁 Java5中引入了新的锁机制——Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接 ...

  3. (删)Java线程同步实现二:Lock锁和Condition

    在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...

  4. Java中的Lock锁

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

  5. 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 ...

  6. Java并发--lock锁详解

    在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方 ...

  7. java并发lock锁详解和使用

    一.synchronized的缺陷 synchronized是java中的一个关键字,也就是说是Java语言内置的特性.那么为什么会出现Lock呢? 在上面一篇文章中,我们了解到如果一个代码块被syn ...

  8. java中同步(synchronized)详解

    一.开山篇: 1.synchronized的使用 一个程序中,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题.在Java中内置了语言级的同步原语--synchronized,这也大大简 ...

  9. 【高并发】面试官:Java中提供了synchronized,为什么还要提供Lock呢?

    写在前面 在Java中提供了synchronized关键字来保证只有一个线程能够访问同步代码块.既然已经提供了synchronized关键字,那为何在Java的SDK包中,还会提供Lock接口呢?这是 ...

随机推荐

  1. HTML5与类有关的扩充

    对于传统HTML而言,HTML5是一个叛逆.所有之前的版本对JavaScript接口的描述都不过三言两语,主要篇幅都用于定义标记,与JavaScript相关的内容一概交由DOM规范去定义. 而HTML ...

  2. CentOS 7.9 网络配置

    vi /etc/sysconfig/network-scripts/ifcfg-ens33 (45条消息) CentOS 7.9 网络配置_$青的博客-CSDN博客_centos7.9网卡配置

  3. JSTL详解(常用标签以及c:forEach遍历集合)

    JSTL标签 一. JSTL的简介 1. 什么是JSTL 2. JSTL常用标签库 3. JSTL使用步骤 二. 核心标签库常用标签 1. c: set 标签 2. c: out 标签 3. c: i ...

  4. python修改Gsettings的配置文件

    GSettings 的配置文件是 xml 格式的,文件需以 .gschema.xml 结尾,文件名通常与 id 相同.配置文件安装在 /usr/share/glib-2.0/schemas/ 目录下, ...

  5. 【Java分享客栈】SpringBoot整合WebSocket+Stomp搭建群聊项目

    前言 前两周经常有大学生小伙伴私信给我,问我可否有偿提供毕设帮助,我说暂时没有这个打算,因为工作实在太忙,现阶段无法投入到这样的领域内,其中有两个小伙伴又问到我websocket该怎么使用,想给自己的 ...

  6. 一. 为什么要用SpringMVC框架

    以前是怎么做项目的.CoreServlet,起到一个中心处理器作用.所有的请求到服务器,服务器给CoreServlet,在里面处理所有表的增删改查,跳转也在里面做.以前做部门就是 DepServlet ...

  7. SpringMVC获取请求参数-POJO类型参数

    1.Controller中的业务方法的POJO参数的属性名与请求参数一致,参数值会自动映射匹配 1.创建POJO类 public class User { private String usernam ...

  8. 解决pycharm的爬虫乱码问题(初步了解各种编码格式)

    Ascii码(American Standard Code for Information Interchange,美国信息互换标准代码):最初计算机只在美国使用时,只用8位的字节来组合出256(2的 ...

  9. 另类终端「GitHub 热点速览 v.22.15」

    作者:HelloGitHub-小鱼干 除了编译器之外,终端也是我们日常打交道的软件之一.但,你用它看过股票吗?OpenBBTerminal 不仅能让你看股票,还能让你用科学的方法进行股票投资.说到投资 ...

  10. 从小白到侠客的 Windows 快捷键宝典

    "天下 武功,唯快不破."你是否羡慕过那些电脑键盘侠客,他们操作起电脑行云流水,任务完成的又快又准.这到底是怎么做到的呢?我们是否也能向他们一样达到把键盘操作熟记于心呢?那就跟着笔 ...