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()获取当前指针位置,写入数据时会覆盖目标位置原有数据。常见应用为大文件断点续传(基于文件分片,上传失败后仅传未成功分片),可通过该类合并文件分片。

相关推荐
jnrjian17 小时前
Oracle username 集成 AD
数据库·oracle
super_lzb17 小时前
mybatis拦截器ResultSetHandler详解
java·spring·mybatis·springboot
施嘉伟17 小时前
Oracle重建控制文件技术总结
数据库·oracle
li星野17 小时前
Windows下QT开发一个OpenCV程序
学习
代码or搬砖17 小时前
JVM垃圾回收器
java·jvm·算法
小光学长17 小时前
基于ssm的美容院会员管理系统xnbnpp45(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库
客卿12317 小时前
C语言刷题--合并有序数组
java·c语言·算法
Qhumaing17 小时前
C++学习:【PTA】数据结构 7-1 实验6-1(图-邻接矩阵)
c++·学习·算法