Java技术八股学习Day14

乐观锁和悲观锁的定义

乐观锁假设多线程访问共享资源时冲突概率极低,核心思想是 "先操作后校验",不加锁直接执行操作,仅在提交时通过特定机制(如 CAS、版本号)判断资源是否被修改,若未修改则执行成功,否则重试或返回失败;悲观锁则假设冲突必然发生,核心思想是 "先加锁再操作",访问资源前先获取锁,确保同一时间仅一个线程能操作资源,其他线程需等待锁释放,本质是通过独占锁规避冲突。

乐观锁和悲观锁的核心区别

两者核心差异集中在冲突假设、实现方式、性能开销和适用场景:乐观锁基于 "冲突少" 假设,无锁设计,依赖 CAS 或版本号校验,并发性能高、无死锁风险,但存在重试开销和 ABA 问题;悲观锁基于 "冲突多" 假设,通过独占锁(如 synchronized、数据库行锁)实现,能直接规避冲突,无需重试,但锁竞争会导致线程阻塞,存在死锁风险,并发性能较低。

悲观锁的常见实现方式

悲观锁的实现分为 Java 层面和数据库层面:Java 中主要通过synchronized关键字(隐式锁,自动释放)、ReentrantLock类(显式锁,需手动释放)实现,二者均为独占锁,能保证同一时间仅一个线程执行临界区代码;数据库中主要是行锁(如 InnoDB 的行级锁)、表锁(如 MyISAM 的表级锁),通过 SQL 执行时自动加锁或手动加锁(如select ... for update),确保数据操作的原子性。

乐观锁的常见实现方式

乐观锁无实际 "锁" 的概念,核心通过 "校验机制" 实现,主要有两种方式:一是 CAS(Compare and Swap,比较并交换),底层依赖 CPU 原子指令,需传入内存地址、预期值、新值三个参数,仅当内存中实际值与预期值一致时才替换为新值,Java 中AtomicIntegerAtomicReference等原子类均基于 CAS 实现;二是版本号机制,数据库场景中常用,给表新增version字段(或时间戳字段),更新数据时对比版本号(如update table set ... where id=? and version=?),一致则更新并自增版本号,不一致则说明资源被修改,需重试或返回失败。

CAS 的原理与存在的问题

CAS 的核心是 CPU 提供的原子指令(如 x86 的cmpxchg),能保证 "比较 - 替换" 操作的原子性,避免并发问题;Java 中的 CAS 通过Unsafe类直接调用底层 native 方法实现,依赖硬件支持。CAS 存在三个核心问题:一是 ABA 问题(内存值从 A 变为 B 再变回 A,CAS 误判为未修改);二是循环时间长开销大(自旋 CAS 失败后会持续重试,高冲突场景下占用大量 CPU 资源);三是只能保证单个变量的原子操作,无法直接实现多个变量的原子性组合操作。

IO 流简介

IO(Input/Output)即输入与输出,数据输入到计算机内存的过程为输入,输出到外部存储(如文件、数据库)的过程为输出,数据传输类似水流故称为 IO 流。Java IO 流分为输入流与输出流,按数据处理方式又分为字节流(处理原始字节)和字符流(处理字符,避免乱码),所有 IO 流类均派生自四个抽象基类:字节输入流基类InputStream、字符输入流基类Reader、字节输出流基类OutputStream、字符输出流基类Writer

字节流

字节流直接操作原始字节数据,适用于音频、图片等媒体文件。InputStream是所有字节输入流的父类,提供read()(读字节)、skip()(跳过字节)、close()(关闭流)等方法,Java9 后新增readAllBytes()(读所有字节)、transferTo()(传输到输出流)等实用方法,常用实现类有FileInputStream(直接读取文件字节)、DataInputStream(读取指定类型数据,需结合其他流)、ObjectInputStream(反序列化读取 Java 对象,类需实现Serializable接口,transient修饰属性不序列化)。OutputStream是所有字节输出流的父类,提供write()(写字节)、flush()(刷新缓冲)、close()(关闭流)等方法,常用实现类有FileOutputStream(直接写入文件字节)、DataOutputStream(写入指定类型数据)、ObjectOutputStream(序列化写入 Java 对象)。

字符流

字符流由 JVM 将字节转换为字符,默认采用 Unicode 编码,可自定义编码,适用于文本文件,避免字节流处理字符时的乱码问题(字节流无编码感知)。Reader是所有字符输入流的父类,提供read()(读字符)、skip()(跳过字符)等方法,常用实现类有InputStreamReader(字节流转字符流的桥梁)、FileReader(封装InputStreamReader,直接读取字符文件)。Writer是所有字符输出流的父类,提供write()(写字符 / 字符串)、append()(附加字符)、flush()(刷新缓冲)等方法,常用实现类有OutputStreamWriter(字符流转字节流的桥梁)、FileWriter(封装OutputStreamWriter,直接写入字符到文件)。

缓冲流

IO 操作消耗性能,缓冲流通过内部缓冲区(字节数组)一次性读取 / 写入多个字节,减少频繁 IO 操作,提高传输效率,采用装饰器模式增强普通流功能。字节缓冲流包括BufferedInputStream(增强字节输入流)和BufferedOutputStream(增强字节输出流),默认缓冲区大小 8192 字节,可自定义;字符缓冲流包括BufferedReader(增强字符输入流)和BufferedWriter(增强字符输出流),内部同样维护缓冲区。当使用单字节 / 字符读写方法时,缓冲流与普通流性能差距极大(缓冲流耗时仅为普通流的 1/165 左右);使用字节数组 / 字符数组读写时,两者性能差距较小。

打印流

打印流用于便捷输出数据,分为字节打印流PrintStream和字符打印流PrintWriterPrintStreamOutputStream的子类,System.out本质是PrintStream对象,print()/println()方法底层调用其write()方法;PrintWriterWriter的子类,功能与PrintStream类似,更适合处理字符数据。

随机访问流

随机访问流RandomAccessFile支持跳转到文件任意位置读写,需指定读写模式(r只读、rw读写、rws同步更新内容和元数据、rwd同步更新内容)。其内部通过文件指针标记下一个读写位置,可通过seek(long pos)设置指针偏移量,getFilePointer()获取当前指针位置,写入数据时会覆盖目标位置原有数据。常见应用为大文件断点续传(基于文件分片,上传失败后仅传未成功分片),可通过该类合并文件分片。

相关推荐
Database_Cool_15 分钟前
单机 MySQL 迁移到分布式数据库方便吗?阿里云 PolarDB-X 100% MySQL 协议兼容零改造平滑迁移
数据库·分布式·mysql
姜太小白1 小时前
【MySQL】 索引优化实战:解决 WHERE 等值 + IS NULL 查询,TEXT 字段报错 1167 的完整指南
数据库·mysql
a1117761 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
窝子面1 小时前
手搓最简前后端协作-node
javascript·数据库
我的xiaodoujiao2 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
吳所畏惧2 小时前
宝塔面板Redis密码修改指南:SSH命令修改 vs 面板UI界面修改,哪个更靠谱?
运维·服务器·数据库·redis·缓存·ssh
DFT计算杂谈2 小时前
无 Root 权限在 Tesla K80 零门槛部署 DeepSeek 大模型
linux·服务器·网络·数据库·机器学习
HPFBoy2 小时前
log4net 数据库存日志正确配置
数据库
liuxiaowei32 小时前
【绝版教程】新版MySQL DBA高级实战进阶班 MySQL8.0 姜承尧-腾讯数据库总监
数据库·mysql·dba