当前位置: 首页 > web >正文

07_Java中的锁

文章目录

    • 一、java中的锁
      • 1. synchronized关键字
      • 2. ReentrantLock
      • 3. ReadWriteLock
      • 4. StampedLock
      • 5. Semaphore(信号量)
    • 二、重点解析
      • 1. synchronized修饰静态方法与普通方法的区别
        • synchronized修饰普通方法
        • synchronized修饰静态方法
        • 总结
      • 2. 公平锁和非公平锁的区别及实现机制
        • 2.1 公平锁与非公平锁的区别
        • 2.2 实现机制
        • 2.3 CAS操作
      • 3. 乐观锁与悲观锁
        • 一、乐观锁原理
        • 二、悲观锁原理
        • 三、总结

一、java中的锁

以下是一些Java中常见的锁及其使用示例:

1. synchronized关键字

synchronized是Java内置的一种锁机制,可以用于方法或代码块。

示例:synchronized方法

public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}public static void main(String[] args) {SynchronizedExample example = new SynchronizedExample();// 多个线程同时调用increment方法,会被同步锁保护}
}

示例:synchronized代码块

public class SynchronizedBlockExample {private final Object lock = new Object();private int count = 0;public void increment() {synchronized (lock) {count++;}}public int getCount() {return count;}
}

2. ReentrantLock

ReentrantLock提供了比synchronized更灵活的锁机制。

示例:ReentrantLock

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}

3. ReadWriteLock

ReadWriteLock允许多个读线程同时访问资源,但写线程在访问资源时会独占锁。

示例:ReadWriteLock

import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();private int count = 0;public void increment() {rwLock.writeLock().lock();try {count++;} finally {rwLock.writeLock().unlock();}}public int getCount() {rwLock.readLock().lock();try {return count;} finally {rwLock.readLock().unlock();}}
}

4. StampedLock

StampedLock提供了三种锁模式:写锁、读锁和乐观读锁。

示例:StampedLock

import java.util.concurrent.locks.StampedLock;public class StampedLockExample {private final StampedLock stampedLock = new StampedLock();private int count = 0;public void increment() {long stamp = stampedLock.writeLock();try {count++;} finally {stampedLock.unlockWrite(stamp);}}public int getCount() {long stamp = stampedLock.tryOptimisticRead();int currentCount = count;if (!stampedLock.validate(stamp)) {stamp = stampedLock.readLock();try {currentCount = count;} finally {stampedLock.unlockRead(stamp);}}return currentCount;}
}

5. Semaphore(信号量)

Semaphore允许你指定一个许可数量,只有持有许可的线程才能访问资源。

示例:Semaphore

import java.util.concurrent.Semaphore;public class SemaphoreExample {private final Semaphore semaphore = new Semaphore(3); // 允许3个线程同时访问public void accessResource() {try {semaphore.acquire(); // 获取许可// 访问共享资源的代码} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release(); // 释放许可}}
}

这些示例展示了Java中不同的锁机制及其基本用法。在实际应用中,你需要根据具体的场景和需求选择合适的锁机制。

二、重点解析

1. synchronized修饰静态方法与普通方法的区别

[在Java中,synchronized关键字可以用于修饰方法,以控制对共享资源的并发访问。当synchronized修饰静态方法和普通方法时,它们之间存在一些关键的区别。下面我将详细解释这些区别:

synchronized修饰普通方法

synchronized修饰一个普通方法时,锁的是调用该方法的对象实例。这意味着,同一个类的不同对象实例之间的同步方法是互不干扰的。换句话说,一个线程可以访问一个对象实例的同步方法,而另一个线程可以同时访问同一个类的另一个对象实例的同步方法。

代码示例

public class SynchronizedInstanceMethod {public synchronized void doSomething() {// 同步代码块}
}// 使用示例
SynchronizedInstanceMethod obj1 = new SynchronizedInstanceMethod();
SynchronizedInstanceMethod obj2 = new SynchronizedInstanceMethod();// 线程1可以访问obj1的doSomething方法
// 线程2可以同时访问obj2的doSomething方法,因为锁的是不同的对象实例
synchronized修饰静态方法

synchronized修饰一个静态方法时,锁的是调用该方法的类的Class对象。这意味着,对于同一个类的所有对象实例,它们的静态同步方法是互斥的。换句话说,一个线程访问某个类的静态同步方法时,其他线程不能访问同一个类的任何其他静态同步方法(但可以访问该类的非静态同步方法,因为非静态同步方法锁的是对象实例)。

代码示例

public class SynchronizedStaticMethod {public static synchronized void doSomethingStatic() {// 同步代码块}public synchronized void doSomethingInstance() {// 同步代码块}
}// 使用示例
SynchronizedStaticMethod obj1 = new SynchronizedStaticMethod();
SynchronizedStaticMethod obj2 = new SynchronizedStaticMethod();// 线程1访问doSomethingStatic方法时
// 线程2不能同时访问doSomethingStatic方法(因为锁的是类对象)
// 但线程2可以访问obj1或obj2的doSomethingInstance方法(因为锁的是对象实例)
总结
  • synchronized修饰普通方法时,锁的是对象实例。
  • synchronized修饰静态方法时,锁的是类的Class对象。

理解这些区别对于正确设计多线程程序至关重要,因为它直接影响到程序的并发行为和性能。希望这能帮助你更好地理解synchronized关键字在Java中的使用!

2. 公平锁和非公平锁的区别及实现机制

java架构师{type=“agent” src=“https://lingjing-online.cdn.bcebos.com/v1/lingjing-online/agent_logo/2025-03-19/69dbf7f5-8a72-4520-bce3-743773826ed2.JPEG?x-bce-process=image/format,f_webp”} 在Java并发编程中,锁是用于控制多个线程对共享资源访问的重要机制。锁有两种常见的策略:公平锁(Fair Lock)和非公平锁(Non-Fair Lock)。下面我将详细介绍这两种锁的区别及实现机制。

2.1 公平锁与非公平锁的区别
  1. 获取锁的顺序

    • 公平锁:多个线程在获取锁时,按照先后顺序来获取,遵循“先到先得”的原则。也就是说,当多个线程请求同一个锁时,锁会按照线程请求的顺序进行分配,等待时间最长的线程将优先获取锁。
    • 非公平锁:不保证线程按照请求锁的顺序来获取锁。每个线程在请求锁时,都会直接尝试获取锁,如果锁可用则立即获取,不能获取时再进入队列排队。这种方式更加“激进”,允许后来的线程可能会比之前的线程先获得锁。
  2. 性能

    • 公平锁:由于公平锁需要维护一个严格的等待队列,并在每次获取锁时检查队列,判断当前线程是否在队列的前端,这增加了锁获取的开销,导致性能相对较低。
    • 非公平锁:由于非公平锁不需要维护一个严格的等待队列,线程可以直接尝试获取锁,减少了锁竞争和上下文切换的开销,因此在高并发场景下性能通常优于公平锁。
  3. 线程饥饿问题

    • 公平锁:通过按顺序分配锁,确保每个线程最终都会获得锁,不会导致某些线程一直处于等待状态,从而避免线程饥饿现象。
    • 非公平锁:由于非公平锁允许后来的线程插队,某些线程可能长时间无法获取锁,导致线程饥饿问题。
  4. 适用场景

    • 公平锁:适合在对线程公平性要求较高的场合,如银行转账、物流系统等需要按顺序处理的业务场景。
    • 非公平锁:因其高性能在大多数场景下被优先采用,尤其是对性能要求较高的应用场景。
2.2 实现机制
  1. 公平锁的实现

    • 在Java中,ReentrantLock可以通过构造函数设置为公平锁,例如:ReentrantLock lock = new ReentrantLock(true);(其中true表示公平锁)。
    • 当线程请求锁时,公平锁会将这些请求放入一个队列中(如AbstractQueuedSynchronizer中的队列),按照队列的顺序依次为线程分配锁。
  2. 非公平锁的实现

    • 在Java中,ReentrantLock默认是非公平锁,例如:ReentrantLock lock = new ReentrantLock();
    • 当线程请求锁时,非公平锁会直接尝试抢占锁,如果锁没有被其他线程持有,当前线程会立即获取锁。
    • 非公平锁的实现通常依赖于CAS(Compare-And-Swap)操作或者通过一个简单的循环尝试来获取锁。

综上所述,公平锁和非公平锁在获取锁的顺序、性能、线程饥饿问题以及适用场景方面存在显著差异。在实际开发中,应根据具体需求选择合适的锁策略。

2.3 CAS操作

在CAS(Compare-And-Swap)操作中,确实会涉及三个关键的操作数:内存位置V、预期原值A和新值B。操作的流程是这样的:

‌比较‌:首先,会比较内存位置V当前存储的值与预期原值A。
‌交换‌:如果这两个值相等,说明没有其他线程在这段时间内修改过这个值,那么就将内存位置V的值更新为新值B。
‌不做操作‌:如果这两个值不相等,说明有其他线程已经修改过这个值,那么当前线程就不会进行任何操作,通常会重新读取内存位置V的当前值,并可能再次尝试CAS操作,或者采取其他同步机制。
这个机制的关键在于它的原子性,即比较和交换这两个步骤是不可分割的,要么全部执行,要么都不执行,这样就不会出现线程安全问题。

3. 乐观锁与悲观锁

在Java中,乐观锁与悲观锁是两种常见的并发控制策略,它们各自有着不同的原理和应用场景。下面我将对这两种锁的原理进行详细讲解。

一、乐观锁原理

乐观锁的核心思想是“先操作,再检查”。它假设在大多数情况下,多个线程之间不会发生冲突,因此不会主动加锁。在更新数据时,乐观锁会使用一种“检测机制”来判断是否真的发生了冲突。

  1. 版本号机制:这是乐观锁常用的一种实现方式。在数据库中,可以为每个数据行添加一个版本号字段。当线程读取数据时,会同时读取该数据的版本号。在更新数据时,会比较当前数据的版本号与读取时的版本号是否一致。如果一致,说明没有其他线程修改过数据,可以安全地提交更新;如果不一致,说明数据已被其他线程修改,此时会进行冲突处理,通常是重新读取数据并尝试更新。
  2. CAS操作:在Java中,乐观锁还可以通过CAS(Compare-And-Swap,比较并交换)操作来实现。CAS是一种无锁的操作,它会比较当前值和期望值是否一致,如果一致就更新新值,否则就失败并重试。这种方式适用于对变量进行原子更新的场景。

乐观锁适用于读操作频繁而写操作较少的场景,可以减少锁的使用,提高并发性能。同时,由于乐观锁不会主动加锁,因此不会出现死锁问题。但是,如果并发冲突频率较高,可能会导致频繁的重试,反而降低性能。

二、悲观锁原理

与乐观锁相反,悲观锁的核心思想是“先加锁,再操作”。它假设在多线程环境下,对共享资源的访问会产生冲突,因此默认认为每次访问都会发生冲突,需要加锁来保证独占访问。

  1. 数据库锁机制:在数据库中,悲观锁通常通过SELECT … FOR UPDATE语句来实现。当一个线程执行该语句时,数据库会给查询的那条数据加锁,其他线程无法修改这条数据,直到锁被释放。这种方式适用于对数据库记录进行独占访问的场景。
  2. Java同步锁:在Java中,悲观锁可以通过synchronized关键字或ReentrantLock来实现。当一个线程进入synchronized修饰的方法或代码块时,或显式地获取ReentrantLock锁时,其他线程将被阻塞,直到该线程退出同步块或释放锁。这种方式适用于对共享变量进行同步访问的场景。

悲观锁能够完全避免并发冲突,因为加锁后其他线程无法访问数据。在冲突频率较高的场景下(如多个线程频繁修改数据的场景),悲观锁的性能更稳定。但是,由于加锁会导致其他线程等待,因此悲观锁可能会降低系统吞吐量。同时,如果锁没有正确释放,可能会导致死锁问题。

三、总结

乐观锁和悲观锁各有优缺点,适用于不同的场景。在选择使用哪种锁时,需要根据具体的业务需求和并发情况来进行权衡。如果读操作频繁而写操作较少,且对并发性能要求较高,可以选择乐观锁;如果写操作频繁或并发冲突较多,需要保证数据的一致性和线程安全性,可以选择悲观锁。

http://www.xdnf.cn/news/5870.html

相关文章:

  • 系统平衡与企业挑战
  • Tomcat与纯 Java Socket 实现远程通信的区别
  • 中国人工智能智能体研究报告
  • Linux的文件查找与压缩
  • 关于cleanRL Q-learning
  • Java集合框架详解与使用场景示例
  • MySQL 5.7在CentOS 7.9系统下的安装(下)——给MySQL设置密码
  • Android NDK 高版本交叉编译:为何无需配置 FLAGS 和 INCLUDES
  • org.slf4j.MDC介绍-笔记
  • 集成DHTMLX 预订排期调度组件实践指南:如何实现后端数据格式转换
  • web 自动化之 yaml 数据/日志/截图
  • Boundary Attention Constrained Zero-Shot Layout-To-Image Generation
  • 配置hadoop集群-启动集群
  • apache2的默认html修改
  • 【前端三剑客】Ajax技术实现前端开发
  • ETL 数据集成平台与数据仓库的关系及 ETL 工具推荐
  • 前端流行框架Vue3教程:15. 组件事件
  • kafka----初步安装与配置
  • PROFIBUS DP转ModbusTCP网关模块于污水处理系统的成功应用案例解读​
  • C++中的各式类型转换
  • 序列化和反序列化(hadoop)
  • RabbitMQ发布订阅模式深度解析与实践指南
  • 解决 CentOS 7 镜像源无法访问的问题
  • 爬虫请求频率应控制在多少合适?
  • cocos creator 3.8 下的 2D 改动
  • Kubernetes Horizontal Pod Autosscaler(HPA)核心机制解析
  • 【android bluetooth 框架分析 02】【Module详解 6】【StorageModule 模块介绍】
  • C#进阶(1) ArrayList
  • TDengine编译成功后的bin目录下的文件的作用
  • 【计算机组成原理】第二部分 存储器--分类、层次结构