首页 > 其他分享 >Synchronized 和 Lock 的区别和使用场景

Synchronized 和 Lock 的区别和使用场景

时间:2023-12-15 11:33:28浏览次数:23  
标签:场景 Synchronized Lock 获取 线程 lock 方法

Synchronized 和 Lock的概念

Synchronized 是Java 并发编程中很重要的关键字,另外一个很重要的是 volatile。Syncronized 的目的是一次只允许一个线程进入由他修饰的代码段,从而允许他们进行自我保护。Synchronized 很像生活中的锁例子,进入由Synchronized 保护的代码区首先需要获取 Synchronized 这把锁,其他线程想要执行必须进行等待。Synchronized 锁住的代码区域执行完成后需要把锁归还,也就是释放锁,这样才能够让其他线程使用。

Lock 是 Java并发编程中很重要的一个接口,它要比 Synchronized 关键字更能直译"锁"的概念,Lock需要手动加锁和手动解锁,一般通过 lock.lock() 方法来进行加锁, 通过 lock.unlock() 方法进行解锁。与 Lock 关联密切的锁有 ReetrantLock 和 ReadWriteLock。

ReetrantLock 实现了Lock接口,它是一个可重入锁,内部定义了公平锁与非公平锁。

ReadWriteLock 一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。ReentrantReadWirteLock实现了ReadWirteLock接口,并未实现Lock接口。

Synchronized 和 Lock 的使用

Synchronized 和 Lock 的使用:

下面是 Synchronized 的例子:

在方法上使用 Synchronized

方法声明时使用,放在范围操作符之后,返回类型声明之前。即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候。

private int number;
public synchronized void numIncrease(){
  number++;
}

在某个代码段使用 Synchronized

你也可以在某个代码块上使用 Synchronized 关键字,表示只能有一个线程进入某个代码段。

public void numDecrease(Object num){

 

synchronized (num){
number++;
}
}

使用 Synchronized 锁住整个对象

synchronized后面括号里是一对象,此时线程获得的是对象锁。

public void test() {
synchronized (this) {
// ...
}
}

下面是 Lock 的例子:

Lock是一个接口,它主要由下面这几个方法

public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}

对上面 Lock 接口的方法做一个简单的解释:

lock(): lock 方法可能是平常使用最多的一个方法,就是用来获取锁。如果锁被其他线程获取,则进行等待。

如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。

Lock lock = ...;
lock.lock();
try{
//处理任务
}catch(Exception ex){

}finally{
lock.unlock(); //释放锁
}

tryLock() :方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

tryLock(long time, TimeUnit unit) 方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。

Lock lock = ...;
if(lock.tryLock()) {
try{
//处理任务
}catch(Exception ex){

}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}

lockInterruptibly() : 此方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就是说,当两个线程同时通过 lock.lockInterruptibly() 想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用 threadB.interrupt() 方法能够中断线程B的等待过程。

由于 lockInterruptibly() 的声明中抛出了异常,所以 lock.lockInterruptibly() 必须放在try块中或者在调用lockInterruptibly() 的方法外声明抛出 InterruptedException。一般形式如下:

public void method() throws InterruptedException {
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}

一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为本身在前面的文章中讲过单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。因此当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的。而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。

Synchronized 和 Lock 的主要区别

Synchronzied 和 Lock 的主要区别如下:

  • 存在层面:Syncronized 是Java 中的一个关键字,存在于 JVM 层面,Lock 是 Java 中的一个接口
  • 锁的释放条件:1. 获取锁的线程执行完同步代码后,自动释放;2. 线程发生异常时,JVM会让线程释放锁;Lock 必须在 finally 关键字中释放锁,不然容易造成线程死锁
  • 锁的获取: 在 Syncronized 中,假设线程 A 获得锁,B 线程等待。如果 A 发生阻塞,那么 B 会一直等待。在 Lock 中,会分情况而定,Lock 中有尝试获取锁的方法,如果尝试获取到锁,则不用一直等待
  • 锁的状态:Synchronized 无法判断锁的状态,Lock 则可以判断
  • 锁的类型:Synchronized 是可重入,不可中断,非公平锁;Lock 锁则是 可重入,可判断,可公平锁
  • 锁的性能:Synchronized 适用于少量同步的情况下,性能开销比较大。Lock 锁适用于大量同步阶段:
    • Lock 锁可以提高多个线程进行读的效率(使用 readWriteLock)
    • 在竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;
    • ReetrantLock 提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。

标签:场景,Synchronized,Lock,获取,线程,lock,方法
From: https://www.cnblogs.com/guoyu1/p/17903032.html

相关文章

  • C# lock示例
    这两天项目加了个需求,需要给PLC发心跳信号我又不想在原来的循环中加,所以想着再弄个timer来定时发信号。只是这样会有一个问题,就是冲突。两个线程之间,有可能同时与plc发生通讯,引起数据混乱。privatevoidbutton1_Click(objectsender,EventArgse){......
  • 【HTB】Sherlocks-Bumblebee 蓝队 easy
    Task1题目:外部承包商的用户名是什么?外部承包商通过访客WiFi访问了Forela的内部论坛,他们似乎窃取了管理用户的凭据!通过内部论坛窃取管理用户的凭据,首先需要注册一个普通用户。通过访客WiFi访问,ip肯定是内网ip1、进入sqlite3tar-zxvfincident.tgz#解压sqlit......
  • .net core 分布式锁 之 基于 Redis 的 RedLock
    使用场景分布式锁的业务场景涉及到并发控制、任务调度、缓存更新、分布式事务和防止重复操作等方面,能够保证分布式系统的数据一致性和正确性。并发控制:当多个线程或进程同时访问共享资源时,使用分布式锁可以确保只有一个线程或进程能够访问该资源,避免数据竞争和并发冲突。分......
  • SpringBoot中@Transactional失效场景
    一、背景:1、需求  定时器需要定时到“消息通知表”中获取“消息反馈表”中不存在的数据,遍历这些数据,并对每一条数据发起流程,不管发起成功与否都需要往消息反馈表中插入一条该数据的发起结果,若发起成功还需要往“核查案件表”中插入一条该案件的主表数据2、问题:  发现在发......
  • 模型放置到3D场景中后模型位置与鼠标选中的位置不一致怎么办?
    在线工具推荐:3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.jsAI自动纹理开发包 - YOLO虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎1、问题从事3D建模相关工作的朋友们在工作中经常会遇到以下几种问题:1、模型旋......
  • redis stream的所有方法以及用处和使用场景
    目录一、用途:将消息添加到Stream中。二、用途:按范围获取消息。三、用途:阻塞读取消息,支持多个Stream。四、用途:创建消费者组。五、用途:阻塞读取消息并将其分配给消费者组中的消费者。六、用途:确认消息已被消费。七、用途:获取待处理的消息列表。八、用途:删除消息。九、用......
  • [INFLUXDB] 查询数据时,INFLUXDB报“InfluxDBException: user is locked”
    1问题描述通过QueryAPI查询INFLUXDB数据库数据时,查询失败,日志中报INFLUXDB数据库错误:...org.influxdb.InfluxDBException:userislocked atorg.influxdb.InfluxDBException.buildExceptionFromErrorMessage(InfluxDBException.java:161)~[influxdb-java-2.22.jar!/:?]......
  • CompletableFuture + LinkedBlockingDeque 实现生产者消费者案例
    设计要求:1.设计一个生产者生产,消费者消费场景;2.使用线程池 CompletableFuture+队列LinkedBlockingDeque实现;3.生产者生产的数据存储到长度为5的LinkedBlockingDeque队列,消费者消费从LinkedBlockingDeque队列中取数据;4.生产者和消费者均是多线程且不知道谁快谁慢,互......
  • 网页爬虫有哪些常见的应用场景
    网页爬虫是一种自动化程序,能够模拟人类对网页进行访问和数据收集,具有高效、快速、自动化的特点。因此,在各个领域中都有着广泛的应用。以下是网页爬虫的常见应用场景:1.搜索引擎:网页爬虫被广泛应用于搜索引擎,如谷歌、百度等。搜索引擎利用爬虫定期抓取互联网上的网页内容,并建立索引,以......
  • 大语言模型LLM的核心技术及应用场景案例的分析
    LLM的核心技术:自注意力机制(Self-Attention)是LLM中的关键组成部分。它允许模型在不同输入序列元素之间分配不同的权重,通过计算查询(Query)、键(Key)和值(Value)之间的相互关系,以便更好地捕捉长距离依赖关系。例如,在处理一段对话时,自注意力机制能帮助模型理解哪些词汇是关键信息,哪些是上下......