天增的博客
首页
博客
  • 分布式解决方案
  • Java并发工具包
  • redis
  • LeetCode
  • 系统设计
  • JVM体系
Github (opens new window)
Rss (opens new window)
  • zh-CN
  • en-US
首页
博客
  • 分布式解决方案
  • Java并发工具包
  • redis
  • LeetCode
  • 系统设计
  • JVM体系
Github (opens new window)
Rss (opens new window)
  • zh-CN
  • en-US
  • Java并发工具包
  • 并发基础
    • 线程基础
      • Thread的状态
      • 进程与线程
      • 正确停止线程的方式
      • Thread的实现方式
      • waitnotifynotifyAll
      • 生产者消费者模型
    • 线程安全
      • 线程不安全
      • 线程安全
      • 需要注意线程安全问题的情况
  • 并发工具
    • 线程协作
      • Semaphore信号量
      • CountDownLatch详解
      • 使用CompletableFuture解决旅游平台问题
      • 使用CyclicBarrier解决团建问题
    • Future
      • Future主要功能
      • FutureTask源码分析
    • ThreadLocal
      • ThreadLocal内存泄漏
      • ThreadLocal使用场景
    • 原子类
      • 原子类的作用概览
      • 原子类的性能分析
    • 阻塞队列
      • 常见的阻塞队列
      • 阻塞队列的常用方法
      • 什么是阻塞队列
    • 并发容器
      • HashMap
      • CopyOnWriteArrayList
      • ConcurrentHashMap详解
    • 线程池
      • 为什么多线程会带来性能问题
      • 线程池的优势
      • 创建线程池的参数
        • 如何设置线程数
      • 线程池线程复用原理
      • ForkJoin框架
    • 各种锁
      • 锁的种类和特点
        • 公平锁非公平锁
        • 自旋锁非自旋锁
        • 共享锁独占锁
        • 乐观锁和悲观锁
      • JVM锁优化
      • synchronized和Lock的对比
        • 相同点
        • 不同点
        • 如何选择
      • lock的常用方法
  • 底层原理
    • CAS原理
    • AQS框架
    • 伪共享
    • java内存模型
      • Java内存模型介绍
      • happens-before规则
  • topic
  • Java并发工具包
  • 并发工具
  • 各种锁
  • synchronized和Lock的对比
2022-04-21
目录

synchronized和Lock的对比

# synchronized和Lock的对比

# 相同点

# 用来保护资源安全

最基本的作用

# 都可以保证可见性

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

绘图2

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

LockHappensBefor

# 都可重入

可重入指的是某个线程如果已经获得了一个锁,现在试图再次请求这个它已经获得的锁,如果它无需提前释放这个锁,而是直接可以继续使用持有的这个锁。 如果必须释放锁后才能再次申请这个锁,就是不可重入的。

而 synchronized 和 ReentrantLock 都具有可重入的特性。

# 不同点

# 用法不同

  • Lock的加锁方式是显示的。

    必须使用Lock对象来加锁和解锁,通常会在finally中使用unlock进行解锁,以避免出现异常锁无法释放的情况。

  • Synchronized的加锁方式是隐式。

    加锁解锁都不需要手动去控制,仅仅需要声明一下即可。不需要指定锁对象,可以在方法上、也可以在代码中加锁。

# 加解锁顺序不同

  • Lock的加解锁顺序可以自由控制

    如果有多把Lock锁,可以不按照加锁的顺序来反序解锁。如代码所示

    lock1.lock();
    lock2.lock()
    ....
    lock1.unlock();
    lock2.unlock();
    
  • Synchronized的解锁顺序必须和解锁顺序完全相反

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

    因为synchronized的由JVM控制的,在编译的时候会将上列代码解析成大致如下

    monitorenter // 加obj1的锁
      monitorenter // 加obj2的锁
    
      monitorexit // 解obj2的锁
    monitorexit // 解obj2的锁
    

# synchronized锁不够灵活

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

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

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

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

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

# 是否可以设置公平锁

公平锁是指多个线程在等待同一个锁时,根据先来后到的原则依次获得锁。

ReentrantLock 等 Lock 实现类可以根据自己的需要来设置公平或非公平,synchronized 则不能设置。

# 性能区别

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

# 如何选择

  1. 如果能不用最好既不使用 Lock 也不使用 synchronized。

    因为在许多情况下你可以使用 java.util.concurrent 包中的机制,它会为你处理所有的加锁和解锁操作,也就是推荐优先使用工具类来加解锁。

  2. 如果 synchronized 关键字适合你的程序, 那么请尽量使用它,这样可以减少编写代码的数量,减少出错的概率。

    因为一旦忘记在 finally 里 unlock,代码可能会出很大的问题,而使用 synchronized 更安全。

  3. 如果特别需要 Lock 的特殊功能,比如尝试获取锁、可中断、超时功能等,或者是需要实现分布式锁的时候,才使用 Lock。

最近更新
01
以 root 身份启动 transmission-daemon
12-13
02
Debian系统安装qbittorrent-nox
12-09
03
LXC Debain12安装zerotier并实现局域网自动nat转发
07-29
更多文章>
Theme by Vdoing | Copyright © 2015-2024 天增 | 苏ICP备16037388号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式