diff --git a/docs/cs-basics/data-structure/linear-data-structure.md b/docs/cs-basics/data-structure/linear-data-structure.md index cc5cc6a5..e8ae63a1 100644 --- a/docs/cs-basics/data-structure/linear-data-structure.md +++ b/docs/cs-basics/data-structure/linear-data-structure.md @@ -326,9 +326,9 @@ myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty. 当我们需要按照一定顺序来处理数据的时候可以考虑使用队列这个数据结构。 - **阻塞队列:** 阻塞队列可以看成在队列基础上加了阻塞操作的队列。当队列为空的时候,出队操作阻塞,当队列满的时候,入队操作阻塞。使用阻塞队列我们可以很容易实现“生产者 - 消费者“模型。 -- **线程池中的请求/任务队列:** 线程池中没有空闲线程时,新的任务请求线程资源时,线程池该如何处理呢?答案是将这些请求放在队列中,当有空闲线程的时候,会循环中反复从队列中获取任务来执行。队列分为无界队列(基于链表)和有界队列(基于数组)。无界队列的特点就是可以一直入列,除非系统资源耗尽,比如:`FixedThreadPool` 使用无界队列 `LinkedBlockingQueue`。但是有界队列就不一样了,当队列满的话后面再有任务/请求就会拒绝,在 Java 中的体现就是会抛出`java.util.concurrent.RejectedExecutionException` 异常。 -- 栈:双端队列天生便可以实现栈的全部功能(`push`、`pop` 和 `peek`),并且在 Deque 接口中已经实现了相关方法。Stack 类已经和 Vector 一样被遗弃,现在在 Java 中普遍使用双端队列(Deque)来实现栈。 -- 广度优先搜索(BFS),在图的广度优先搜索过程中,队列被用于存储待访问的节点,保证按照层次顺序遍历图的节点。 +- **线程池中的请求/任务队列:** 当线程池中没有空闲线程时,新的任务请求线程资源会被如何处理呢?答案是这些任务会被放入任务队列中,等待线程池中的线程空闲后再从队列中取出任务执行。任务队列分为无界队列(基于链表实现)和有界队列(基于数组实现)。无界队列的特点是队列容量理论上没有限制,任务可以持续入队,直到系统资源耗尽。例如:`FixedThreadPool` 使用的阻塞队列 `LinkedBlockingQueue`,其默认容量为 `Integer.MAX_VALUE`,因此可以被视为“无界队列”。而有界队列则不同,当队列已满时,如果再有新任务提交,由于队列无法继续容纳任务,线程池会拒绝这些任务,并抛出 `java.util.concurrent.RejectedExecutionException` 异常。 +- **栈**:双端队列天生便可以实现栈的全部功能(`push`、`pop` 和 `peek`),并且在 Deque 接口中已经实现了相关方法。Stack 类已经和 Vector 一样被遗弃,现在在 Java 中普遍使用双端队列(Deque)来实现栈。 +- **广度优先搜索(BFS)**:在图的广度优先搜索过程中,队列被用于存储待访问的节点,保证按照层次顺序遍历图的节点。 - Linux 内核进程队列(按优先级排队) - 现实生活中的派对,播放器上的播放列表; - 消息队列 diff --git a/docs/database/redis/redis-questions-01.md b/docs/database/redis/redis-questions-01.md index 3bed4651..e553f773 100644 --- a/docs/database/redis/redis-questions-01.md +++ b/docs/database/redis/redis-questions-01.md @@ -114,6 +114,16 @@ PS:篇幅问题,我这并没有对上面提到的分布式缓存选型做详 Redis 除了可以用作缓存之外,还可以用于分布式锁、限流、消息队列、延时队列等场景,功能强大! +### 为什么用 Redis 而不用本地缓存呢? + +| 特性 | 本地缓存 | Redis | +| ------------ | ------------------------------------ | -------------------------------- | +| 数据一致性 | 多服务器部署时存在数据不一致问题 | 数据一致 | +| 内存限制 | 受限于单台服务器内存 | 独立部署,内存空间更大 | +| 数据丢失风险 | 服务器宕机数据丢失 | 可持久化,数据不易丢失 | +| 管理维护 | 分散,管理不便 | 集中管理,提供丰富的管理工具 | +| 功能丰富性 | 功能有限,通常只提供简单的键值对存储 | 功能丰富,支持多种数据结构和功能 | + ### 常见的缓存读写策略有哪些? 关于常见的缓存读写策略的详细介绍,可以看我写的这篇文章:[3 种常用的缓存读写策略详解](https://javaguide.cn/database/redis/3-commonly-used-cache-read-and-write-strategies.html) 。 diff --git a/docs/java/basis/java-basic-questions-03.md b/docs/java/basis/java-basic-questions-03.md index af8f2274..7bc956f7 100644 --- a/docs/java/basis/java-basic-questions-03.md +++ b/docs/java/basis/java-basic-questions-03.md @@ -89,14 +89,6 @@ Finally **注意:不要在 finally 语句块中使用 return!** 当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。 -[jvm 官方文档](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10.2.5)中有明确提到: - -> If the `try` clause executes a _return_, the compiled code does the following: -> -> 1. Saves the return value (if any) in a local variable. -> 2. Executes a _jsr_ to the code for the `finally` clause. -> 3. Upon return from the `finally` clause, returns the value saved in the local variable. - 代码示例: ```java diff --git a/docs/java/concurrent/java-concurrent-questions-03.md b/docs/java/concurrent/java-concurrent-questions-03.md index de312bcf..9609d4d9 100644 --- a/docs/java/concurrent/java-concurrent-questions-03.md +++ b/docs/java/concurrent/java-concurrent-questions-03.md @@ -276,23 +276,21 @@ TTL 改造的地方有两处: 另外,《阿里巴巴 Java 开发手册》中强制线程池不允许使用 `Executors` 去创建,而是通过 `ThreadPoolExecutor` 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险 -`Executors` 返回线程池对象的弊端如下: +`Executors` 返回线程池对象的弊端如下(后文会详细介绍到): -- `FixedThreadPool` 和 `SingleThreadExecutor`:使用的是有界阻塞队列是 `LinkedBlockingQueue` ,其任务队列的最大长度为 `Integer.MAX_VALUE` ,可能堆积大量的请求,从而导致 OOM。 +- `FixedThreadPool` 和 `SingleThreadExecutor`:使用的是阻塞队列 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可以看作是无界的,可能堆积大量的请求,从而导致 OOM。 - `CachedThreadPool`:使用的是同步队列 `SynchronousQueue`, 允许创建的线程数量为 `Integer.MAX_VALUE` ,如果任务数量过多且执行速度较慢,可能会创建大量的线程,从而导致 OOM。 -- `ScheduledThreadPool` 和 `SingleThreadScheduledExecutor` :使用的无界的延迟阻塞队列 `DelayedWorkQueue` ,任务队列最大长度为 `Integer.MAX_VALUE` ,可能堆积大量的请求,从而导致 OOM。 +- `ScheduledThreadPool` 和 `SingleThreadScheduledExecutor`:使用的无界的延迟阻塞队列`DelayedWorkQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。 ```java -// 有界队列 LinkedBlockingQueue public static ExecutorService newFixedThreadPool(int nThreads) { - + // LinkedBlockingQueue 的默认长度为 Integer.MAX_VALUE,可以看作是无界的 return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue()); } -// 无界队列 LinkedBlockingQueue public static ExecutorService newSingleThreadExecutor() { - + // LinkedBlockingQueue 的默认长度为 Integer.MAX_VALUE,可以看作是无界的 return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue())); } @@ -886,6 +884,7 @@ public FutureTask(Runnable runnable, V result) { `FutureTask`相当于对`Callable` 进行了封装,管理着任务执行的情况,存储了 `Callable` 的 `call` 方法的任务执行结果。 关于更多 `Future` 的源码细节,可以肝这篇万字解析,写的很清楚:[Java是如何实现Future模式的?万字详解!](https://juejin.cn/post/6844904199625375757)。 + ### CompletableFuture 类有什么用? `Future` 在实际使用过程中存在一些局限性比如不支持异步任务的编排组合、获取计算结果的 `get()` 方法为阻塞调用。 diff --git a/docs/java/concurrent/java-thread-pool-best-practices.md b/docs/java/concurrent/java-thread-pool-best-practices.md index 5763e026..04154bfa 100644 --- a/docs/java/concurrent/java-thread-pool-best-practices.md +++ b/docs/java/concurrent/java-thread-pool-best-practices.md @@ -13,7 +13,7 @@ tag: `Executors` 返回线程池对象的弊端如下(后文会详细介绍到): -- **`FixedThreadPool` 和 `SingleThreadExecutor`**:使用的是有界阻塞队列 `LinkedBlockingQueue`,任务队列的默认长度和最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。 +- **`FixedThreadPool` 和 `SingleThreadExecutor`**:使用的是阻塞队列 `LinkedBlockingQueue`,任务队列的默认长度和最大长度为 `Integer.MAX_VALUE`,可以看作是无界队列,可能堆积大量的请求,从而导致 OOM。 - **`CachedThreadPool`**:使用的是同步队列 `SynchronousQueue`,允许创建的线程数量为 `Integer.MAX_VALUE` ,可能会创建大量线程,从而导致 OOM。 - **`ScheduledThreadPool` 和 `SingleThreadScheduledExecutor`** : 使用的无界的延迟阻塞队列`DelayedWorkQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。 diff --git a/docs/java/concurrent/java-thread-pool-summary.md b/docs/java/concurrent/java-thread-pool-summary.md index ec1ae249..48fb23d9 100644 --- a/docs/java/concurrent/java-thread-pool-summary.md +++ b/docs/java/concurrent/java-thread-pool-summary.md @@ -183,21 +183,19 @@ public static class CallerRunsPolicy implements RejectedExecutionHandler { `Executors` 返回线程池对象的弊端如下(后文会详细介绍到): -- `FixedThreadPool` 和 `SingleThreadExecutor`:使用的是无界的 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。 +- `FixedThreadPool` 和 `SingleThreadExecutor`:使用的是阻塞队列 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可以看作是无界的,可能堆积大量的请求,从而导致 OOM。 - `CachedThreadPool`:使用的是同步队列 `SynchronousQueue`, 允许创建的线程数量为 `Integer.MAX_VALUE` ,如果任务数量过多且执行速度较慢,可能会创建大量的线程,从而导致 OOM。 - `ScheduledThreadPool` 和 `SingleThreadScheduledExecutor`:使用的无界的延迟阻塞队列`DelayedWorkQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。 ```java -// 无界队列 LinkedBlockingQueue public static ExecutorService newFixedThreadPool(int nThreads) { - + // LinkedBlockingQueue 的默认长度为 Integer.MAX_VALUE,可以看作是无界的 return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue()); } -// 无界队列 LinkedBlockingQueue public static ExecutorService newSingleThreadExecutor() { - + // LinkedBlockingQueue 的默认长度为 Integer.MAX_VALUE,可以看作是无界的 return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue())); }