前言:

我们主要学习 synchronized 和 Lock 的异同点,以及该如何选择。

相同点

synchronized 和 Lock 的相同点非常多,我们这里重点讲解 3 个比较大的相同点。

  • synchronized 和 Lock 都是用来保护资源线程安全的。

这一点毋庸置疑,这是它们的基本作用。

  • 都可以保证可见性。

对于 synchronized 而言,线程 A 在进入 synchronized 块之前或在 synchronized 块内进行操作,对于后续的获得同一个 monitor 锁的线程 B 是可见的,也就是线程 B 是可以看到线程 A 之前的操作的,这也体现了 happens-before 针对 synchronized 的一个原则。

而对于 Lock 而言,它和 synchronized 是一样,都可以保证可见性,如图所示,在解锁之前的所有操作对加锁之后的所有操作都是可见的。

如果你之前不了解什么是可见性,此时理解可能会有一定的困难,可以在学习本专栏的 Java 内存模型相关内容后,再复习本课时,就会豁然开朗。

  • synchronized 和 ReentrantLock 都拥有可重入的特点。

这里的 ReentrantLock 是 Lock 接口的一个最主要的实现类,在对比 synchronized 和 Lock 的时候,也会选择 Lock 的主要实现类来进行对比。可重入指的是某个线程如果已经获得了一个锁,现在试图再次请求这个它已经获得的锁,如果它无需提前释放这个锁,而是直接可以继续使用持有的这个锁,那么就是可重入的。如果必须释放锁后才能再次申请这个锁,就是不可重入的。而 synchronized 和 ReentrantLock 都具有可重入的特性。

不同点

下面我们来看下 synchronized 和 Lock 的区别,和相同点一样,它们之间也有非常多的区别,这里讲解其中比较大的 7 点不同。

用法区别:

synchronized 关键字可以加在方法上,不需要指定锁对象(此时的锁对象为 this),也可以新建一个同步代码块并且自定义 monitor 锁对象;而 Lock 接口必须显示用 Lock 锁对象开始加锁 lock() 和解锁 unlock(),并且一般会在 finally 块中确保用 unlock() 来解锁,以防发生死锁。

与 Lock 显式的加锁和解锁不同的是 synchronized 的加解锁是隐式的,尤其是抛异常的时候也能保证释放锁,但是 Java 代码中并没有相关的体现。

加解锁顺序不同:

对于 Lock 而言如果有多把 Lock 锁,Lock 可以不完全按照加锁的反序解锁,比如我们可以先获取 Lock1 锁,再获取 Lock2 锁,解锁时则先解锁 Lock1,再解锁 Lock2,加解锁有一定的灵活度,如代码所示。

lock1.lock();
lock2.lock();
...
lock1.unlock();
lock2.unlock();

但是 synchronized 无法做到,synchronized 解锁的顺序和加锁的顺序必须完全相反,例如:

synchronized(obj1){
    synchronized(obj2){
        ...
    }
}

那么在这里,顺序就是先对 obj1 加锁,然后对 obj2 加锁,然后对 obj2 解锁,最后解锁 obj1。这是因为 synchronized 加解锁是由 JVM 实现的,在执行完 synchronized 块后会自动解锁,所以会按照 synchronized 的嵌套顺序加解锁,不能自行控制。

synchronized 锁不够灵活

一旦 synchronized 锁已经被某个线程获得了,此时其他线程如果还想获得,那它只能被阻塞,直到持有锁的线程运行完毕或者发生异常从而释放这个锁。如果持有锁的线程持有很长时间才释放,那么整个程序的运行效率就会降低,而且如果持有锁的线程永远不释放锁,那么尝试获取锁的线程只能永远等下去。

相比之下,Lock 类在等锁的过程中,如果使用的是 lockInterruptibly 方法,那么如果觉得等待的时间太长了不想再继续等待,可以中断退出,也可以用 tryLock() 等方法尝试获取锁,如果获取不到锁也可以做别的事,更加灵活。

  • synchronized 锁只能同时被一个线程拥有,但是 Lock 锁没有这个限制

例如在读写锁中的读锁,是可以同时被多个线程持有的,可是 synchronized 做不到。

  • 原理区别 synchronized 是内置锁,由 JVM 实现获取锁和释放锁的原理,还分为偏向锁、轻量级锁、重量级锁。

Lock 根据实现不同,有不同的原理,例如 ReentrantLock 内部是通过 AQS 来获取和释放锁的。

是否可以设置公平/非公平:

公平锁是指多个线程在等待同一个锁时,根据先来后到的原则依次获得锁。ReentrantLock 等 Lock 实现类可以根据自己的需要来设置公平或非公平,synchronized 则不能设置。

性能区别:

在 Java 5 以及之前,synchronized 的性能比较低,但是到了 Java 6 以后,发生了变化,因为 JDK 对 synchronized 进行了很多优化,比如自适应自旋、锁消除、锁粗化、轻量级锁、偏向锁等,所以后期的 Java 版本里的 synchronized 的性能并不比 Lock 差。

如何选择

讲完了 synchronized 和 Lock 的相同点和区别,最后我们再来看下如何选择它们,

在 Java 并发编程实战和 Java 核心技术里都认为:

  • 如果能不用最好既不使用 Lock 也不使用 synchronized。因为在许多情况下你可以使用 java.util.concurrent 包中的机制,它会为你处理所有的加锁和解锁操作,也就是推荐优先使用工具类来加解锁。
  • 如果 synchronized 关键字适合你的程序, 那么请尽量使用它,这样可以减少编写代码的数量,减少出错的概率。因为一旦忘记在 finally 里 unlock,代码可能会出很大的问题,而使用 synchronized 更安全。
  • 如果特别需要 Lock 的特殊功能,比如尝试获取锁、可中断、超时功能等,才使用 Lock。

到此这篇关于 synchronized 和 Lock 的异同点(如何让选择)的文章就介绍到这了,更多相关 synchronized 和 Lock 区别内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

synchronized 和 Lock 的异同点(如何让选择)的更多相关文章

  1. Swift线程安全详解-概念,三种锁,死锁,Atomic,synchronized

    研究了下,是线程安全问题。UIKit以及Fundation事实上,大多数Cocoa提供的Api都不是线程安全的,尤其是与UI相关的UIKit,只能在主线程上操作。需要线程安全的时候,开发者自己维护就可以了。用来加锁,解锁。关于ObjectiveC参考这篇文章Atomic一个非Atomic的属性在非ARC的时候像这样可以看到,如果在多线程同时set的情况下,可能会造成release两次。Property的Runtime对应的C代码为可以看到,如果是nonatomic的,synchronized可以看看这个S

  2. 【swift Objective-c】深入了解 核心比较语言特性

    ###OC与swiftautoreleasepoolsynchronizedappledoc关于线程安全objc_sync源码

  3. 并发 – 什么是Swift相当于Objective-C的“@synchronized”?

    我搜索了Swift的书,但找不到Swift版本的@synchronized。如何在Swift中实现互斥?它比@synchronized有点更冗长,但作为一个替代品工作得很好:

  4. android – 指令’monitor-enter v1’时出现空指针异常

    堆栈跟踪:解决方法如果localDBUtility为null,则无法对其进行同步调用.您只能在实例化对象上进行同步.

  5. android – 如何通过MediaRecorder.start()来静音“嘟嘟”?

    解决方法虽然我来不及回答它.它仍然可以帮助所有人都在谷歌搜索同样的问题.在开始媒体记录器之前添加以下两行代码..它会静音手机声音..启动录音机后等待一到两秒钟并取消静音,你可以使用以下可运行的…

  6. js中值类型和引用类型的区别介绍

    这篇文章介绍了js中值类型和引用类型的区别,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  7. PHP中$GLOBALS与global的区别详解

    今天小编就为大家分享一篇关于PHP中$GLOBALS与global的区别详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  8. jQuery和AngularJS的区别浅析

    这篇文章主要介绍了jQuery和AngularJS的区别浅析,本文着重讲解一个熟悉jQuery开的程序员如何应对AngularJS中的一些编程思想的转变,需要的朋友可以参考下

  9. Vue.js和Vue.runtime.js区别浅析

    这篇文章主要介绍了Vue.js和Vue.runtime.js区别浅析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. PHP7引入的"??"和"?:"的区别讲解

    今天小编就为大家分享一篇关于PHP7引入的"??"和"?:"的区别讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部