synchronized 和 Lock 的异同点(如何让选择)

目录
  • 相同点
  • 不同点
  • 如何选择

前言:

我们主要学习 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 区别内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 多线程Synchronized和Lock的区别

    引言 在多线程中,为了使线程安全,我们经常会使用synchronized和Lock进行代码同步和加锁,但是具体两者有什么区别,什么场景下适合用什么可能还不大清楚,主要的区别大致如下: 区别 1.synchronized是java关键字,而Lock是java中的一个接口 2.synchronized会自动释放锁,而Lock必须手动释放锁 3.synchronized是不可中断的,Lock可以中断也可以不中断 4.通过Lock可以知道线程有没有拿到锁,而synchronized不能 5.synchr

  • 浅谈Java中Lock和Synchronized的区别

    目录 1. 从功能角度来看 2. 从特性来看 3. 从性能方面来看 1. 从功能角度来看 Lock和Synchronized都是java中去用来解决线程安全问题的一个工具 2. 从特性来看 Synchronized是java中的同步关键字,Lock是J.U.C包中提供的接口,而这个接口有很多的实现类,包括ReentrantLock这样重入锁的实现,Synchronized可以通过两种方式去控制锁的力度 一种把synchronized关键字修饰在方法层面,另一种是修饰在代码块上,可以通过synch

  • Java常用锁synchronized和ReentrantLock的区别

    目录 区别1:用法不同 synchronized 基础使用 ReentrantLock 基础使用 区别2:获取锁和释放锁方式不同 区别3:锁类型不同 区别4:响应中断不同 区别5:底层实现不同 小结 前言: 在 Java 中,常用的锁有两种:synchronized(内置锁)和 ReentrantLock(可重入锁),二者的功效都是相同得,但又有很多不同点,所以我们今天就来聊聊. 区别1:用法不同 synchronized 可用来修饰普通方法.静态方法和代码块,而 ReentrantLock 只

  • 简单了解synchronized和lock的区别

    这篇文章主要介绍了简单了解synchronized和lock的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 类别 synchronized Lock 存在层次 Java的关键字 一个类 锁的释放 1.以获取锁的线程执行代码同步代码,释放锁. 2,线程执行发生异常,jvm会让线程释放锁 在finally中必须释放锁,不然容易造成线程死锁 锁的获取 假设A线程获得锁,B线程等待,如果A线程阻塞,则B会一直等 分情况而定,Lock有多个获取锁的

  • 通过实例解析synchronized和lock区别

    1,原始构成 synchronized是关键字,属于JVM层面,通过wait,notify和notifyAll来调度线程. Lock是具体类,是api层面的锁. 2,使用方法 synchronized不需要用户手动去释放锁, 当synchronized代码执行完后,系统会自动释放锁. Lock需要用户手动释放锁,否则会出现死锁现象.需要lock和unlock配合try/finally语句块来完成. 3,等待是否中断 synchronized不可中断,除非抛出异常或者正常运行完毕. Lock可中断

  • 详谈Lock与synchronized 的区别

    1.lock是可中断锁,而synchronized 不是可中断锁 线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定, 如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断 如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情 ReentrantLock获取锁定与三种方式: a)  lock(),如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁 b) tr

  • 深入Synchronized和java.util.concurrent.locks.Lock的区别详解

    主要相同点:Lock能完成Synchronized所实现的所有功能.主要不同点:Lock有比Synchronized更精确的线程予以和更好的性能.Synchronized会自动释放锁,但是Lock一定要求程序员手工释放,并且必须在finally从句中释放.synchronized 修饰方法时 表示同一个对象在不同的线程中 表现为同步队列如果实例化不同的对象 那么synchronized就不会出现同步效果了.1.对象的锁 所有对象都自动含有单一的锁. JVM负责跟踪对象被加锁的次数.如果一个对象被

  • Java编程synchronized与lock的区别【推荐】

    前言 本文介绍了Java编程synchronized与lock的区别的相关内容,如果您对synchronized与lock不太了解,这两篇文章 或许是不错的选择: Java 同步锁(synchronized)详解及实例 Java多线程基础--Lock类 正文 从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?这个问题

  • 浅谈Synchronized和Lock的区别

    如下所示: Synchronized是内置的java关键字,Lock是一个java类. Synchronized无法判断是否获取到了锁,Lock可以判断是否获取到了锁. Synchronized会自动释放锁,Lock必须手动释放锁. Synchronized线程1获得锁之后阻塞,等待锁的线程2会一直等下去(死等).Lock不一定会死等. Synchronized可重入锁.不可中断.非公平锁.Lock是可重入锁.选择是否可中断.可以选择是否公平. Synchronized适合锁少量的代码同步问题.

  • synchronized 和 Lock 的异同点(如何让选择)

    目录 相同点 不同点 如何选择 前言: 我们主要学习 synchronized 和 Lock 的异同点,以及该如何选择. 相同点 synchronized 和 Lock 的相同点非常多,我们这里重点讲解 3 个比较大的相同点. synchronized 和 Lock 都是用来保护资源线程安全的. 这一点毋庸置疑,这是它们的基本作用. 都可以保证可见性. 对于 synchronized 而言,线程 A 在进入 synchronized 块之前或在 synchronized 块内进行操作,对于后续的

  • 一文带你搞懂Java中Synchronized和Lock的原理与使用

    目录 1.Synchronized与Lock对比 2.Synchronized与Lock原理 2.1 Synchronized原理 2.2 Lock原理 3.Synchronized与Lock使用 Synchronized Lock 4.相关问题 1.Synchronized与Lock对比 实现方式:Synchronized是Java语言内置的关键字,而Lock是一个Java接口. 锁的获取和释放:Synchronized是隐式获取和释放锁,由Java虚拟机自动完成:而Lock需要显式地调用lo

  • 学习java多线程

    目录 介绍 为什么需要多线程 线程状态转换 线程使用方式 继承 Thread 类 实现 Runnable 接口 实现 Callable 接口 同步代码---Runnable接口方式 同步方法--Runnable接口方法 同步方法---继承方法 synchronized锁机制 死锁 Lock锁机制 介绍 程序(program)是为完成特定任务.用某种语言编写的一组指令的集合.即指一段静态的代码,静态对象. 进程(process)是程序的一次执行过程,或是正在运行的一个程序.是一个动态的过程:有它自

  • Java中关于线程安全的三种解决方式

    三个窗口卖票的例子解决线程安全问题 问题:买票过程中,出现了重票.错票-->出现了线程的安全问题 问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票 如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来,知道线程a操作完ticket时,其他线程才可以开始操作ticket,这种情况即使线程a出现了阻塞,也不能被改变 在Java中,我们通过同步机制,来解决线程的安全问题.(线程安全问题的前提:有共享数据) 方式一:同步代码块 synchroniz

随机推荐