在 Java 中,字节流、字符流和缓冲流的核心区别主要体现在操作数据的单位、适用场景以及性能特性上。以下是具体分析:
1. 字节流(Byte Stream)
操作单位:以字节(8 位二进制) 为单位处理数据,直接操作原始字节数据。
核心类:
输入流:InputStream(抽象基类),常用实现类如 FileInputStream(文件输入)、ByteArrayInputStream(字节数组输入)。
输出流:OutputStream(抽象基类),常用实现类如 FileOutputStream(文件输出)、ByteArrayOutputStream(字节数组输出)。
适用场景:处理二进制数据(非文本数据),如图片、音频、视频、压缩包等。
特点:
直接与底层数据交互,无编码 / 解码过程,数据原样传输。
读写文本时可能出现乱码(因为不同编码的字符占用字节数不同,如 UTF-8 中一个汉字占 3 字节,GBK 中占 2 字节)。
2. 字符流(Character Stream)
操作单位:以字符(16 位 Unicode) 为单位处理数据,本质是对字节流的封装,会自动进行 ...
一、核心构造方法(7 参数)ThreadPoolExecutor 是 Java 线程池的核心实现类,其 7 参数构造方法定义了线程池的核心行为,具体参数如下:
123456789public ThreadPoolExecutor( int corePoolSize, // 1. 核心线程数 int maximumPoolSize, // 2. 最大线程数 long keepAliveTime, // 3. 线程空闲存活时间 TimeUnit unit, // 4. 时间计量单位 BlockingQueue<Runnable> workQueue, // 5. 任务队列 ThreadFactory threadFactory, // 6. 线程工厂 RejectedExecutionHandler handler // 7. 拒绝策略处理器) {}
二、7 个参数详细解析1. 核心线程数(c ...
底层结构与核心特性
底层:基于红黑树(依赖 TreeMap 实现)
特性:元素唯一、有序序(非插入顺序)、无索引、线程不安全
排序:支持自然排序(元素实现 Comparable)或自定义排序(构造 Comparator)
性能:插入 / 查询 / 删除均为 O (log n)
关键方法(表格形式)
方法分类
方法签名
说明
基础操作
boolean add(E e)
添加元素,重复则返回 false
boolean remove(Object o)
删除指定元素,成功返回 true
boolean contains(Object o)
判断元素是否存在
int size()
返回元素个数
boolean isEmpty()
判断集合是否为空
void clear()
清空所有元素
导航操作
E first()
返回最小元素(第一个元素)
E last()
返回最大元素(最后一个元素)
E ceiling(E e)
返回大于等于 e 的最小元素(不存在返回 null)
E floor(E e)
返回小于等 ...
1. 直接用循环复制2. 使用Arrays.copy()3. System.arraycopy() 方法进行复制在自定义动态数组的扩容过程中,常用的元素复制方法有三种:循环复制、Arrays.copyOf() 和 System.arraycopy()。以下是它们的详细解释和区别:
==elements是数组==
1. 直接用循环复制原理:通过 for 循环遍历原数组,将每个元素逐个复制到新数组中。
实现示例:
123456789private void resize() { int newCapacity = elements.length * 2; // 假设扩容为原来的2倍 Object[] newElements = new Object[newCapacity]; // 循环复制元素 for (int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElement ...
一、核心组合的作用Callable 是带返回值的任务接口,ExecutorService 是线程池管理接口,两者结合可实现 多线程并发执行带返回值的任务,兼具线程复用(线程池优势)和结果获取(Callable 优势)的特性。
二、关键组件
**Callable<V>**:
泛型接口,V 为返回值类型。
需重写 call() 方法(任务执行体),支持返回结果和声明异常。
**ExecutorService**:
线程池接口,负责管理线程生命周期和任务调度。
通过 submit(Callable<V>) 方法提交任务,返回 Future<V> 对象。
**Future<V>**:
用于跟踪任务状态,获取 call() 方法的返回值(get() 方法),支持取消任务(cancel())。
三、使用步骤(代码示例)示例 1:单任务提交与结果获取12345678910111213141516171819202122232425262728import java.util.concurrent.*;public class Cal ...
一、核心作用Callable 是带返回值的线程任务接口,配合 FutureTask 可实现 “有返回值的多线程操作”,弥补了 Runnable 无返回值的不足。
二、关键组件
Callable<V> 接口
泛型接口,V 表示返回值类型。
需重写 call() 方法(线程执行体),有返回值且可声明抛出异常。
FutureTask<V> 类
包装 Callable 任务,实现 Runnable 接口(可被线程执行)。
提供 get() 方法,用于获取 call() 的返回值(阻塞等待任务完成)。
三、使用步骤(代码示例)123456789101112131415161718192021222324252627282930313233import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class CallableDemo { publ ...
一、什么是阻塞队列?阻塞队列(BlockingQueue)是 Java 并发包中一种特殊的队列,它在普通队列的基础上增加了 阻塞功能:
当队列满时,生产者线程会阻塞等待(无法插入元素);
当队列空时,取元素的线程会阻塞等待(无法获取元素)。
这种特性使其非常适合 生产者 - 消费者模型,无需手动处理线程同步,简化了多线程协作。
二、核心特性(以代码中的 ArrayBlockingQueue 为例)
有界队列:
ArrayBlockingQueue 是 有界队列(初始化时必须指定容量,如代码中 new ArrayBlockingQueue<>(10) 表示容量为 10),一旦队列满,生产者线程会被阻塞。
阻塞插入与取出:
插入元素:put() 方法在队列满时会阻塞线程,直到队列有空闲位置;
取出元素:take() 方法在队列空时会阻塞线程,直到队列中有元素。
(代码中:生产者用 put() 放数据,消费者用 take() 取数据,自动实现了 “生产 - 消费” 的同步)
三、代码中的生产者 - 消费者模型
生产者(Producter):
循环生成随机整数, ...
一、什么是 AtomicInteger?AtomicInteger 是 Java 并发包(java.util.concurrent.atomic)中的原子类,用于在 多线程环境下安全地进行整数的原子操作(如自增、自减、赋值等),避免了使用 synchronized 锁的性能开销。
二、核心作用解决多线程对整数变量进行 复合操作(如 i++、i = i + 2)时的线程安全问题。
普通 int 的 i++ 是 “读 - 改 - 写” 三步操作,非原子性,多线程并发时会导致结果错误;
AtomicInteger 通过 CAS 机制 保证这些操作的原子性(一步完成,不可打断)。
三、底层实现:CAS 机制CAS(Compare-And-Swap,比较并交换)是一种无锁同步技术,核心逻辑:
读取变量当前值(expectedValue);
计算目标值(newValue);
若当前值仍为 expectedValue(未被其他线程修改),则更新为 newValue;否则重试(自旋),直到成功。
AtomicInteger 内部通过 volatile int value 存储值(保证可见性), ...
一、什么是 volatile?volatile 是 Java 中的一个关键字,用于修饰变量,保证变量的 可见性 和 禁止指令重排序,但不保证原子性。
二、核心特性
可见性
当一个变量被 volatile 修饰时,线程对该变量的修改会 立即被其他线程看到。
原理:线程修改 volatile 变量后,会强制将修改后的值刷新到主内存;其他线程读取时,会直接从主内存加载最新值(而非线程本地缓存)。
示例:
1234567891011121314class VolatileDemo { volatile boolean flag = false; void setFlag() { flag = true; // 修改后立即刷新到主内存 } void checkFlag() { while (!flag) { // 每次循环都会从主内存读取flag最新值 } System.out.println("flag已变为tr ...
什么是锁重入?一个线程拿到锁之后,不用释放,还能再拿同一把锁。
举个例子你进家门(拿到家门锁),进了客厅后,想进卧室(卧室也用同一把钥匙),不用先把家门钥匙还回去,直接用手里的钥匙开门就行。
Java 里的情况
synchronized 关键字
比如一个同步方法 A 里调用了另一个同步方法 B,且两者用的是同一把锁(比如都是同一个对象的锁),线程进了 A 之后,能直接进 B,不用等自己释放 A 的锁。
ReentrantLock 锁
手动加锁时,线程第一次lock()拿到锁后,还能再lock()一次(重入),但要记得解锁同样多次(unlock()),最后一次解锁后,其他线程才能抢。
为什么需要它?避免死锁。如果不支持重入,线程进了 A 之后想进 B,就得先放 A 的锁,可自己还拿着 A 的锁呢,就会卡住(死锁)。
关键点
必须是「同一把锁」才能重入,不同的锁不行。
ReentrantLock 要记得加几次锁就解几次,不然锁会一直被占着。
以下是几个直观的锁重入代码示例,分别展示 synchronized 和 ReentrantLock 的重入特性:
示例 1:synchro ...


