Skip to content

Commit 501c3d9

Browse files
committed
初始代码
1 parent 1a83da3 commit 501c3d9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+28287
-0
lines changed

.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Compiled class file
2+
*.class
3+
4+
# Log file
5+
*.log
6+
7+
# BlueJ files
8+
*.ctxt
9+
10+
# Mobile Tools for Java (J2ME)
11+
.mtj.tmp/
12+
13+
# Package Files #
14+
*.jar
15+
*.war
16+
*.nar
17+
*.ear
18+
*.zip
19+
*.tar.gz
20+
*.rar
21+
22+
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23+
hs_err_pid*
24+
25+
target
26+
.classpath
27+
.factorypath
28+
.project
29+
.settings
30+
.resourceCache

README.md

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
# async-file
2+
3+
#### 介绍
4+
框架提供Java异步读写文件功能,除依赖slf4j日志包,不依赖其他第三方包。Java应用程序引入框架可以简单的,异步和非阻塞的读写文件。框架包含三个工具类:
5+
6+
7+
[`AIOFileReader`](src/main/java/net/kava/file/reader/AIOFileReader.java) 异步读取文件,使用Java NIO库 [`AsynchronousFileChannel`](https://docs.oracle.com/javase/10/docs/api/java/nio/channels/AsynchronousFileChannel.html)[`CompletionHandler`](https://docs.oracle.com/javase/10/docs/api/java/nio/channels/CompletionHandler.html) 实现。
8+
9+
[`AIOFileWriter`](src/main/java/net/kava/file/reader/AIOFileWriter.java) 异步写入文件,使用Java NIO库 [`AsynchronousFileChannel`](https://docs.oracle.com/javase/10/docs/api/java/nio/channels/AsynchronousFileChannel.html)[`CompletionHandler`](https://docs.oracle.com/javase/10/docs/api/java/nio/channels/CompletionHandler.html) 实现。
10+
11+
[`NIOFileLineReader`](src/main/java/net/kava/file/reader/NIOFileLineReader.java) 非阻塞读取文件,使用 [`ForkJoinPool`](https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/ForkJoinPool.html)[`BufferedReader`](https://docs.oracle.com/javase/10/docs/api/java/io/BufferedReader.html) 实现
12+
13+
Java提供的 [`Files`](https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html) 文件读取功能是阻塞的。
14+
15+
#### 安装教程
16+
17+
首先,如果项目使用Maven工具,在项目的pom.xml文件中添加依赖
18+
19+
```xml
20+
<dependency>
21+
<groupId>net.kava</groupId>
22+
<artifactId>kava-async-file</artifactId>
23+
<version>1.0.0</version>
24+
</dependency>
25+
```
26+
27+
如果是Gradle项目,需要添加依赖:
28+
29+
```groovy
30+
implementation 'net.kava:kava-async-file:1.0.0'
31+
```
32+
33+
#### 使用说明
34+
35+
[`AIOFileReader`](src/main/java/net/kava/file/reader/AIOFileReader.java)方法列表:
36+
37+
`Query<byte[]> bytes(Path file)` : 读取文件,返回文件数据字节数组,读取的大小有默认缓冲区决定。
38+
39+
`Query<byte[]> allBytes(Path file)` : 读取文件,返回文件所有数据字节数组。每次按默认缓冲区读取文件,完成后合并。
40+
41+
`Query<String> line(Path file)` : 读取文件,返回文件行字符串。每次按默认缓冲区读取文件数据字节数组,按换行符分割字节数组。
42+
43+
`Query<String> allLines(Path file)` : 读取文件,返回文件所有数据字符串。每次按默认缓冲区读取文件数据字节数组,合并后转换成字符串。
44+
45+
默认缓冲区大小定义:
46+
47+
```java
48+
public static final int BUFFER_SIZE = 4096 * 4;
49+
```
50+
51+
示例:
52+
53+
```java
54+
// 按行读取文件,并输出到控制台
55+
final Path FILE = Paths.get("src", "test", "resources", "fileWithmanyOfLine.txt");
56+
AIOFileReader.line(FILE).subscribe((data, err) -> {
57+
if (err != null) {
58+
// 处理异常,如记录日志
59+
err.printStackTrace();
60+
}
61+
62+
if (data != null) {
63+
// 文件行处理,如输出到控制台
64+
System.out.println(data);
65+
}
66+
})
67+
// 等待所有行处理完成
68+
.join();
69+
```
70+
71+
示例:
72+
73+
```java
74+
// 统计文件中单词个数,并找出次数最多的单词
75+
final Path FILE = Paths.get("src", "test", "resources", "fileToCount.txt");
76+
77+
final int MIN = 5;
78+
final int MAX = 10;
79+
80+
ConcurrentHashMap<String, Integer> words = new ConcurrentHashMap<>();
81+
AIOFileReader.line(FILE)
82+
// 过滤掉前14行
83+
.filter(line -> !line.trim().isEmpty()).skip(14)
84+
// 使用空格分隔
85+
.flatMapMerge(line -> Query.of(line.split(" ")))
86+
// 过滤单词
87+
.filter(word -> word.length() > MIN && word.length() < MAX)
88+
// 统计单词次数
89+
.onNext((w, err) -> words.merge(w, 1, Integer::sum))
90+
// 阻塞,直到文件统计完毕
91+
.blockingSubscribe();
92+
93+
Map.Entry<String, ? extends Number> common = Collections.max(words.entrySet(),
94+
Comparator.comparingInt(e -> e.getValue().intValue()));
95+
assertEquals("Hokosa", common.getKey());
96+
assertEquals(183, common.getValue().intValue());
97+
```
98+
99+
示例:
100+
101+
```java
102+
// 统计“*** END OF ”行之前所有单词的数量
103+
// 当读取到"*** END OF "行时,读线程会取消读操作,避免继续读取不需要处理的数据
104+
105+
final Path FILE = Paths.get("src", "test", "resources", "fileToCount.txt");
106+
107+
int[] count = { 0 };
108+
AIOFileReader.line(FILE)
109+
// 过滤空行
110+
.filter(line -> !line.trim().isEmpty())
111+
// 忽略前14行
112+
.skip(14)
113+
// 忽略掉‘*** END OF ’以后的行
114+
.takeWhile(line -> !line.contains("*** END OF "))
115+
// 行按空格切割成单词
116+
.flatMapMerge(line -> Query.of(line.split("\\W+")))
117+
// 去重
118+
.distinct()
119+
// 统计数量
120+
.onNext((word, err) -> {
121+
if (err == null)
122+
count[0]++;
123+
})
124+
// 显示处理中的异常
125+
.onNext((word, err) -> {
126+
if (err != null)
127+
err.printStackTrace();
128+
})
129+
// 阻塞,知道文件读取完成
130+
.blockingSubscribe();
131+
assertEquals(5206, count[0]);
132+
```
133+
134+
示例:
135+
136+
```java
137+
// 详细演示takeWhile的功能:
138+
// 1. 控制台输出前部文件内容,框架日志提示[Cancel file reading. [16384 bytes] has been readed],读取操作取消,不在读取文件数据。
139+
// 2. [16384 bytes] 信息中,16384是框架默认读取缓冲区大小,由此可以判断:文件只读取了一次
140+
final Path FILE = Paths.get("src", "test", "resources", "fileWithmanyOfLine.txt");
141+
142+
int[] count = { 0 };
143+
AIOFileReader.line(FILE)
144+
// 控制台输出
145+
.onNext((data, err) -> {
146+
if (err != null) {
147+
err.printStackTrace();
148+
}
149+
150+
if (data != null) {
151+
System.out.println("before:" + data);
152+
}
153+
})
154+
// 终止文件读操纵。
155+
.takeWhile(line -> false)
156+
.onNext((data, err) -> {
157+
if (err != null) {
158+
err.printStackTrace();
159+
}
160+
161+
if (data != null) {
162+
System.out.println("after:" +data);
163+
}
164+
}).blockingSubscribe();
165+
166+
assertEquals(0, count[0]);
167+
```
168+
169+
示例:
170+
171+
```java
172+
// 也可以使用cancel方法中断读文件操作
173+
174+
final Path FILE = Paths.get("src", "test", "resources", "fileWithmanyOfLine.txt");
175+
176+
CompletableFuture<Void> future = AIOFileReader.line(FILE).subscribe((data, err) -> {
177+
if (err != null) {
178+
System.out.println("error:" + err.getMessage());
179+
}
180+
try {
181+
TimeUnit.MILLISECONDS.sleep(10);
182+
} catch (InterruptedException e) {
183+
e.printStackTrace();
184+
}
185+
System.out.println(Thread.currentThread().getName());
186+
});
187+
188+
TimeUnit.MILLISECONDS.sleep(1000);
189+
190+
future.cancel(false);
191+
```
192+
193+
示例:
194+
195+
```java
196+
// 显示读文件线程的名称
197+
final Path FILE = Paths.get("src", "test", "resources", "fileWithmanyOfLine.txt");
198+
199+
AIOFileReader.bytes(FILE).subscribe((data, err) -> {
200+
if (err != null) {
201+
err.printStackTrace();
202+
}
203+
System.out.println(Thread.currentThread().getName());
204+
}).join();
205+
```
206+
207+
输出结果如下:
208+
209+
```text
210+
Thread-8
211+
Thread-7
212+
Thread-8
213+
Thread-7
214+
Thread-8
215+
Thread-7
216+
Thread-8
217+
Thread-7
218+
Thread-8
219+
Thread-7
220+
...
221+
```
222+
223+
其结果表明:有两个线程读取文件,线程交替读取以保证读取文件数据的顺序,是 [`AsynchronousFileChannel`](https://docs.oracle.com/javase/10/docs/api/java/nio/channels/AsynchronousFileChannel.html) 实现的
224+
225+
226+
[`AIOFileWriter`](src/main/java/net/kava/file/reader/AIOFileReader.java)方法列表:
227+
228+
`CompletableFuture<Integer> write(Path file, byte[] bytes)` : 字节数组数据写入文件。
229+
230+
`CompletableFuture<Integer> write(Path file, String line)` : 字符串数据写入文件。
231+
232+
`CompletableFuture<Integer> write(Path file, Query<String> lines)` : 字符串流数据写入文件。
233+
234+
`CompletableFuture<Integer> write(Path file, Iterable<String> lines)` : 字符串集合数据写入文件。
235+
236+
示例:
237+
238+
```java
239+
// 写入字符串
240+
AIOFileWriter.write(Paths.get(FILE_TO_WRITE), "This is file content:你好").join();
241+
```
242+
243+
示例:
244+
245+
```java
246+
// 分割字符串写入
247+
final String content = "This is file content:你好";
248+
249+
AIOFileWriter.write(Paths.get(FILE_TO_WRITE), String.join(System.lineSeparator(), content.split(" "))).join();
250+
```
251+
252+
示例:
253+
254+
```java
255+
// 字符流写入
256+
Query<String> data = Query.of("This is file content:你好")
257+
.flatMapMerge(line -> Query.of(line.split(" ")))
258+
.map((line) -> line + System.lineSeparator());
259+
AIOFileWriter.write(Paths.get(FILE_TO_WRITE), data).join();
260+
```
261+
262+
示例:
263+
264+
```java
265+
// 字符流转换后写入
266+
Query<String> data = Query.of("This is file content:你好")
267+
.flatMapMerge(line -> Query.of(line.split(" ")))
268+
.map((line) -> line + System.lineSeparator());
269+
AIOFileWriter.write(Paths.get(FILE_TO_WRITE), data).join();
270+
```
271+
272+
示例:
273+
274+
```java
275+
// 边读边写
276+
final Path FILE = Paths.get("src", "test", "resources", "fileWithmanyOfLine.txt");
277+
278+
Query<String> reader = AIOFileReader.line(FILE)
279+
// 忽略前2行
280+
.skip(2)
281+
// 过滤掉空行
282+
.filter(line -> !line.isBlank())
283+
// 转换成大写
284+
.map(String::toUpperCase)
285+
// 加入换行符
286+
.map((line) -> line + System.lineSeparator());
287+
AIOFileWriter.write(Paths.get(FILE_TO_WRITE), reader).join();
288+
```
289+
290+
[`NIOFileLineReader`](src/main/java/net/kava/file/reader/NIOFileLineReader.java) 方法列表:
291+
292+
`Query<String> read(Path file)` : 读取文件行。
293+
294+
```java
295+
// 读取文件行并过滤
296+
final Path FILE = Paths.get("src", "test", "resources", "fileWithmanyOfLine.txt");
297+
NIOFileLineReader.read(FILE).filter(line -> !line.trim().isEmpty()).onNext((data, err) -> {
298+
System.out.println(data);
299+
}).blockingSubscribe();
300+
```
301+
302+
303+
关于使用的建议:
304+
305+
1. 文件的异步读写,并不是未了提高文件的读取性能,而是提高文件读取的吞吐量(读取更多的文件,并保持性能,使JVM可以稳定运行)。
306+
2. 在大多数情况下,使用Jdk提供的[`Files`](https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html)或许更合适。
307+
3. 不要为了异步而异步,找到问题所在,也许解决问题的关键不是异步。
308+
309+
性能测试,参考 [`ReadLineBenchmark`](src/test/java/net/kava/file/performance) 。 其他开源项目文件读写的性能测试 [`ReadFileBenchmark`](https://gitee.com/yangyunjiao/learn-java/blob/master/core-java/core-java-io/src/main/java/net/learnjava/ReadFileBenchmark.java)
310+
311+
312+
建议使用优先级: `Java NIO Files` > `NIOFileLineReader` > `AIOFileReader`
313+
314+
#### 其他开源项目
315+
316+
1. [RxIo](https://github.com/javasync/RxIo)
317+
318+

0 commit comments

Comments
 (0)