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

 更新时间:2023年04月19日 15:50:13   作者:玄明Hanko  
这篇文章主要为大家详细介绍了Java中Synchronized和Lock的原理与使用,文中的示例代码讲解详细,对我们学习Java有一定的帮助,需要的可以参考一下

1、Synchronized与Lock对比

  • 实现方式:Synchronized是Java语言内置的关键字,而Lock是一个Java接口。
  • 锁的获取和释放:Synchronized是隐式获取和释放锁,由Java虚拟机自动完成;而Lock需要显式地调用lock()方法获取锁,并且必须在finally块中调用unlock()方法来释放锁。
  • 可中断性:在获取锁的过程中,如果线程被中断,synchronized会抛出InterruptedException异常并且自动释放锁,而Lock则需要手动捕获InterruptedException异常并处理,同时也支持非阻塞、可轮询以及定时获取锁的方式。
  • 公平性:Synchronized不保证线程获取锁的公平性,而Lock可以通过构造函数指定公平或非公平锁。
  • 锁状态:Synchronized无法判断锁的状态,而Lock可以通过tryLock()、isLocked()来判断锁的状态(线程是否可能取到锁、锁是否被占用等)。
  • 粒度:Synchronized锁的粒度较粗,只能锁住整个方法或代码块,而Lock可以细粒度地控制锁的范围,比如锁某个对象的部分属性。
  • 场景:如果在简单的并发场景下,推荐使用Synchronized;而在需要更高级的锁控制时,可以考虑使用Lock。

一般情况建议使用Synchronized,在JDK1.5之前Lock优于Synchronized,但在JDK1.5之后对Synchronized进行了优化,后面在性能方面基本与Lock一样且使用简单(有作者说"Synchronized是亲生的,JDK还是会一直优化他不会让Lock优于它")。

2、Synchronized与Lock原理

2.1 Synchronized原理

Synchronized是Java语言中最常用的同步机制之一,它可以确保多个线程访问共享资源时的互斥性和可见性。Synchronized关键字的原理如下:

  • Synchronized使用了内置锁(也称为监视器锁)来实现同步。每个Java对象都有一个内置锁,当该对象作为锁被获取时,其他试图获取该锁的线程会被阻塞,直到该锁被释放。
  • Synchronized的锁是与对象相关联的。当一个线程进入Synchronized代码块时,它必须先获取该对象的锁才能执行代码,否则就会被阻塞。当该线程退出Synchronized代码块时,它会自动释放该对象的锁。
  • Synchronized具有可重入性。如果当前线程已经获得了某个对象的锁,那么它可以继续访问该对象的其他Synchronized代码块,而不会被自己持有的锁所阻塞。
  • Synchronized还具有volatile变量的读写语义。在使用Synchronized关键字时,内存屏障会确保本地线程中修改过的变量值被刷新回主内存,从而保证了多个线程之间对变量修改的可见性。

Synchronized通过使用内置锁、与对象关联的锁、可重入性以及内存屏障等机制来实现线程的同步和锁的管理,以保证对共享资源的访问具有互斥性和可见性。

2.2 Lock原理

Lock是Java语言中的一种高级同步机制,它提供了比Synchronized更加灵活和可扩展的同步特性。Lock机制的原理如下:

  • Lock使用了对象的锁来实现同步。每个Lock对象都有一个锁,当该锁被获取时,其他试图获取该锁的线程会被阻塞,直到该锁被释放。
  • Lock的锁是与对象无关的。相比于Synchronized关键字,Lock提供了更加灵活的方式来控制锁的获取和释放。例如,它支持可中断的获取锁操作、超时获取锁操作等等。因此,在需要手动控制锁的获取和释放时,Lock是一个很好的选择。
  • Lock还具有可重入性。如果当前线程已经获得了某个Lock对象的锁,那么它可以继续访问该对象的其他Lock代码块,而不会被自己持有的锁所阻塞。
  • Lock使用了条件变量来实现线程的等待和通知。Condition接口提供了await()、signal()和signalAll()等方法,用于线程之间的等待和通知,从而避免了Object类中wait()和notify()方法可能出现的信号丢失问题。

Lock通过使用对象的锁、与对象无关的锁、可重入性以及条件变量等机制来实现线程的同步和锁的管理,以保证对共享资源的访问具有互斥性和可见性。与Synchronized关键字相比,Lock提供了更加灵活和可扩展的同步特性,但也需要更多的代码来控制锁的获取和释放。

3、Synchronized与Lock使用

下面分别给出Synchronized和Lock的使用示例。

Synchronized

    public class Counter {
        private int count;

        public synchronized void increment() {
            count++;
        }
    }

上述代码定义了一个计数器类Counter,方法都使用了synchronized关键字来实现线程同步。

Lock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}

上述代码也是定义了一个计数器类Counter,但是使用的是Lock接口来实现线程同步。在这种情况下,需要先创建一个ReentrantLock对象,然后在需要同步的代码块中调用lock()方法获取锁,在finally块中调用unlock()方法释放锁。

总的来说,Synchronized更加简单易用,适合用于一些简单的并发场景;而Lock提供了更多的灵活性和可扩展性,适合用于一些复杂的并发场景。

4、相关问题

1)Synchronized和Lock有什么区别?

  • 实现方式:Synchronized是Java内置的关键字,而Lock是一个接口。
  • 锁的获取和释放:Synchronized的获取和释放锁由JVM自动完成,而Lock需要手动调用lock()方法获取锁并在finally块中调用unlock()方法释放锁。
  • 可中断性:如果线程在获取锁的过程中被中断,Synchronized会抛出InterruptedException异常并自动释放锁,而Lock需要手动处理这种情况。
  • 公平性:Synchronized不保证公平性,而Lock可以通过构造函数指定公平或非公平锁。
  • 粒度:Synchronized锁的粒度比较粗,只能锁住整个方法或代码块,而Lock可以细粒度地控制锁的范围。

2)Synchronized的实现原理是什么?

Synchronized是基于Java对象头的监视器(Monitor)实现的。每个Java对象都有一个监视器,同步块的进入和退出需要获取和释放对象的监视器。当线程尝试进入一个被锁住的同步块时,它会先尝试获取对象的监视器锁,如果锁已经被占用,线程就会进入阻塞状态,直到锁被释放。

3)Lock的实现原理是什么?

Lock的实现是基于Java的AbstractQueuedSynchronizer(AQS)框架的。Lock接口定义了多个获取和释放锁的方法,其中比较重要的是lock()和unlock()方法。当一个线程调用lock()方法获取锁时,如果锁未被占用,则该线程会占用锁并继续执行;否则,该线程会进入阻塞状态,直到锁被释放。当一个线程调用unlock()方法释放锁时,会通知等待队列中的其他线程继续尝试获取锁。

4)什么是可重入锁?

可重入锁指的是同一个线程在持有锁的情况下,能够再次获取该锁,而不会导致死锁。Synchronized和ReentrantLock都是可重入锁。可重入锁通过记录持有锁的线程和重入次数,来避免死锁的发生。

5)ReentrantLock为什么比Synchronized更灵活?

ReentrantLock比Synchronized更灵活主要因为它提供了以下功能:

  • 可以指定公平锁或非公平锁。
  • 支持获取锁的超时时间。
  • 支持可中断的获取锁操作。
  • 可以通过tryLock()方法尝试获取锁,如果锁已经被占用,则返回false。
  • 支持多个Condition对象,可以让线程在不同的条件下等待和唤醒。

6)什么是锁自旋?

锁自旋是一种优化锁竞争的技术,它用于减少线程在获取锁时的等待时间。当一个线程请求获取某个对象的锁时,如果此时锁已经被其他线程占用,那么该线程会进入阻塞状态等待锁的释放。而使用锁自旋技术,线程在发现锁被其他线程占用时,并不会立即进入阻塞状态,而是执行一段循环代码(称为自旋),等待锁的持有者释放锁。

以上就是一文带你搞懂Java中Synchronized和Lock的原理与使用的详细内容,更多关于Java Synchronized Lock的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot项目 文件上传临时目标被删除异常的处理方案

    SpringBoot项目 文件上传临时目标被删除异常的处理方案

    这篇文章主要介绍了SpringBoot项目 文件上传临时目标被删除异常的处理方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别及设定serverTimezone的方法

    com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区

    这篇文章主要介绍了com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别以及设定serverTimezone的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • Spring中@Autowired自动注入map详解

    Spring中@Autowired自动注入map详解

    这篇文章主要介绍了Spring中@Autowired自动注入map详解,  spring是支持基于接口实现类的直接注入的,支持注入map,list等集合中,不用做其他的配置,直接注入,需要的朋友可以参考下
    2023-10-10
  • Java并发编程之原子性-Atomic的使用

    Java并发编程之原子性-Atomic的使用

    这篇文章主要介绍了Java并发编程之原子性-Atomic的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • java 基础教程之多线程详解及简单实例

    java 基础教程之多线程详解及简单实例

    这篇文章主要介绍了java 基础教程之多线程详解及简单实例的相关资料,线程的基本属性、如何创建线程、线程的状态切换以及线程通信,需要的朋友可以参考下
    2017-03-03
  • SpringBoot集成Hadoop对HDFS的文件操作方法

    SpringBoot集成Hadoop对HDFS的文件操作方法

    这篇文章主要介绍了SpringBoot集成Hadoop对HDFS的文件操作方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-07-07
  • Spring中的事务管理如何配置

    Spring中的事务管理如何配置

    这篇文章主要介绍了spring中的事务管理如何配置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • 基于Spring框架的Shiro配置方法

    基于Spring框架的Shiro配置方法

    这篇文章主要介绍了基于Spring框架的Shiro配置方法,需要的朋友可以参考下
    2014-10-10
  • Scala之Object的具体使用(小结)

    Scala之Object的具体使用(小结)

    这篇文章主要介绍了Scala之Object的具体使用(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-06-06
  • 详解什么是Java线程池的拒绝策略?

    详解什么是Java线程池的拒绝策略?

    今天给大家总结一下线程池的拒绝策略,文中有非常详细的介绍及代码示例,对正在学习java的小伙伴们有很好地帮助,需要的朋友可以参考下
    2021-05-05

最新评论