mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-06-16 18:10:13 +08:00
Compare commits
9 Commits
a2308d233c
...
6915d159ff
Author | SHA1 | Date | |
---|---|---|---|
|
6915d159ff | ||
|
0f0f46cc50 | ||
|
d48f11c171 | ||
|
1b701c44d2 | ||
|
90dc871ea8 | ||
|
ffcccdca0d | ||
|
e26afb3739 | ||
|
18ea39f934 | ||
|
34eb4d644a |
@ -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 内核进程队列(按优先级排队)
|
||||
- 现实生活中的派对,播放器上的播放列表;
|
||||
- 消息队列
|
||||
|
@ -114,6 +114,16 @@ PS:篇幅问题,我这并没有对上面提到的分布式缓存选型做详
|
||||
|
||||
Redis 除了可以用作缓存之外,还可以用于分布式锁、限流、消息队列、延时队列等场景,功能强大!
|
||||
|
||||
### 为什么用 Redis 而不用本地缓存呢?
|
||||
|
||||
| 特性 | 本地缓存 | Redis |
|
||||
| ------------ | ------------------------------------ | -------------------------------- |
|
||||
| 数据一致性 | 多服务器部署时存在数据不一致问题 | 数据一致 |
|
||||
| 内存限制 | 受限于单台服务器内存 | 独立部署,内存空间更大 |
|
||||
| 数据丢失风险 | 服务器宕机数据丢失 | 可持久化,数据不易丢失 |
|
||||
| 管理维护 | 分散,管理不便 | 集中管理,提供丰富的管理工具 |
|
||||
| 功能丰富性 | 功能有限,通常只提供简单的键值对存储 | 功能丰富,支持多种数据结构和功能 |
|
||||
|
||||
### 常见的缓存读写策略有哪些?
|
||||
|
||||
关于常见的缓存读写策略的详细介绍,可以看我写的这篇文章:[3 种常用的缓存读写策略详解](https://javaguide.cn/database/redis/3-commonly-used-cache-read-and-write-strategies.html) 。
|
||||
|
@ -19,7 +19,7 @@ tag:
|
||||
|
||||
### 消息队列为什么会出现?
|
||||
|
||||
消息队``列算是作为后端程序员的一个必备技能吧,因为**分布式应用必定涉及到各个系统之间的通信问题**,这个时候消息队列也应运而生了。可以说分布式的产生是消息队列的基础,而分布式怕是一个很古老的概念了吧,所以消息队列也是一个很古老的中间件了。
|
||||
消息队列算是作为后端程序员的一个必备技能吧,因为**分布式应用必定涉及到各个系统之间的通信问题**,这个时候消息队列也应运而生了。可以说分布式的产生是消息队列的基础,而分布式怕是一个很古老的概念了吧,所以消息队列也是一个很古老的中间件了。
|
||||
|
||||
### 消息队列能用来干什么?
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<Runnable>());
|
||||
|
||||
}
|
||||
|
||||
// 无界队列 LinkedBlockingQueue
|
||||
public static ExecutorService newSingleThreadExecutor() {
|
||||
|
||||
// LinkedBlockingQueue 的默认长度为 Integer.MAX_VALUE,可以看作是无界的
|
||||
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
|
||||
|
||||
}
|
||||
@ -885,6 +883,8 @@ public FutureTask(Runnable runnable, V result) {
|
||||
|
||||
`FutureTask`相当于对`Callable` 进行了封装,管理着任务执行的情况,存储了 `Callable` 的 `call` 方法的任务执行结果。
|
||||
|
||||
关于更多 `Future` 的源码细节,可以肝这篇万字解析,写的很清楚:[Java是如何实现Future模式的?万字详解!](https://juejin.cn/post/6844904199625375757)。
|
||||
|
||||
### CompletableFuture 类有什么用?
|
||||
|
||||
`Future` 在实际使用过程中存在一些局限性比如不支持异步任务的编排组合、获取计算结果的 `get()` 方法为阻塞调用。
|
||||
|
@ -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。
|
||||
|
||||
|
@ -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<Runnable>());
|
||||
|
||||
}
|
||||
|
||||
// 无界队列 LinkedBlockingQueue
|
||||
public static ExecutorService newSingleThreadExecutor() {
|
||||
|
||||
// LinkedBlockingQueue 的默认长度为 Integer.MAX_VALUE,可以看作是无界的
|
||||
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来
|
||||
## 字节流
|
||||
|
||||
### InputStream(字节输入流)
|
||||
|
||||
|
||||
`InputStream`用于从源头(通常是文件)读取数据(字节信息)到内存中,`java.io.InputStream`抽象类是所有字节输入流的父类。
|
||||
|
||||
`InputStream` 常用方法:
|
||||
@ -430,7 +430,7 @@ class BufferedInputStream extends FilterInputStream {
|
||||
|
||||
### BufferedOutputStream(字节缓冲输出流)
|
||||
|
||||
`BufferedOutputStream` 将数据(字节信息)写入到目的地(通常是文件)的过程中不会一个字节一个字节的写入,而是会先将要写入的字节存放在缓存区,并从内部缓冲区中单独写入字节。这样大幅减少了 IO 次数,提高了读取效率
|
||||
`BufferedOutputStream` 将数据(字节信息)写入到目的地(通常是文件)的过程中不会一个字节一个字节的写入,而是会先将要写入的字节存放在缓存区,并从内部缓冲区中单独写入字节。这样大幅减少了 IO 次数,提高了效率
|
||||
|
||||
```java
|
||||
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
|
||||
|
@ -83,7 +83,7 @@ tag:
|
||||
**Docker 思想**:
|
||||
|
||||
- **集装箱**:就像海运中的集装箱一样,Docker 容器包含了应用程序及其所有依赖项,确保在任何环境中都能以相同的方式运行。
|
||||
- **标准化:**运输方式、存储方式、API 接口。
|
||||
- **标准化**:运输方式、存储方式、API 接口。
|
||||
- **隔离**:每个 Docker 容器都在自己的隔离环境中运行,与宿主机和其他容器隔离。
|
||||
|
||||
### Docker 容器的特点
|
||||
|
Loading…
x
Reference in New Issue
Block a user