线程 - I/O 流
northboat 2/28/2021 Java
多线程基础
Process(进程)与Thread(线程)
进程=指令执行序列+资源
线程创建
推荐使用Runnerable接口
继承Thread类
实现了Runnerable接口,每个线程的优先级和操作系统有关
1、自定义线程类继承Thread类
2、重写run()方法,编写线程执行体
3、创建线程对象,调用start()方法启动线程
public class TestThread extends Thread{
@Override
public void run() {
for(int i = 0; i < 1000; i++){
System.out.println("我在看代码");
}
}
public static void main(String[] args) {
TestThread tt = new TestThread();
//调用start方法,两个线程是同时执行的
tt.start();
for(int i = 0; i < 1000; i++){
System.out.println("我在拉屎");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如何导入外部jar包
1、在com同级目录下新建lib目录,将jar包粘贴进去
2、添加lib为library
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.net.URL;
public class TestThread2 extends Thread{
private String url;
private String name;
public TestThread2(String url, String name){
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownloader wd = new WebDownloader();
wd.downloader(this.url, this.name);
System.out.println("正在下载:" + this.name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://img-blog.csdnimg.cn/2019080416083639.png", "图片1.jpg");
TestThread2 t2 = new TestThread2("https://img-blog.csdnimg.cn/2019080416083639.png", "图片2.jpg");
TestThread2 t3 = new TestThread2("https://img-blog.csdnimg.cn/2019080416083639.png", "图片3.jpg");
TestThread2 t4 = new TestThread2("https://img-blog.csdnimg.cn/2019080416083639.png", "图片4.jpg");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//在lib中导入了外部jar包
class WebDownloader{
public void downloader(String url, String name){
try{
FileUtils.copyURLToFile(new URL(url), new File(name));
}catch(Exception e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
实现Runnerable接口
public class TestThread3 implements Runnable {
@Override
public void run() {
for(int i = 0; i < 1200; i++){
System.out.println("我在拉屎" + i);
}
}
public static void main(String[] args) {
TestThread3 t = new TestThread3();
new Thread(t).start();
for(int i = 0; i < 1200; i++){
System.out.println("我在吃饭" + i);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
实现Callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TestCallable implements Callable<String> {
private String url;
private String name;
public TestCallable(String url, String name){
this.url = url;
this.name = name;
}
@Override
public String call() {
WebDownloader wd = new WebDownloader();
wd.downloader(this.url, this.name);
System.out.println("正在下载:" + this.name);
return name+"下载完成";
}
public static void main(String[] args) {
TestCallable t1 = new TestCallable("https://img-blog.csdnimg.cn/2019080416083639.png", "图片1.jpg");
TestCallable t2 = new TestCallable("https://img-blog.csdnimg.cn/2019080416083639.png", "图片2.jpg");
TestCallable t3 = new TestCallable("https://img-blog.csdnimg.cn/2019080416083639.png", "图片3.jpg");
TestCallable t4 = new TestCallable("https://img-blog.csdnimg.cn/2019080416083639.png", "图片4.jpg");
//创建线程池服务
ExecutorService ser = Executors.newFixedThreadPool(4);
//提交线程服务,在池中执行call方法
Future<String> f1 = ser.submit(t1);
Future<String> f2 = ser.submit(t2);
Future<String> f3 = ser.submit(t3);
Future<String> f4 = ser.submit(t4);
//获取call函数的返回结果
try{
String res1 = f1.get();
String res2 = f2.get();
String res3 = f3.get();
String res4 = f4.get();
}catch(Exception e){
e.printStackTrace();
}
//停止线程池
ser.shutdownNow();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
并发
多个线程操作一个对象
public class TestThread4 implements Runnable{
private int tickets = 100;
public void run(){
while(true){
if(tickets <= 0)
break;
System.out.println(TestThread2.currentThread().getName() + "抢到了第" + tickets-- + "张票");
}
}
public static void main(String[] args) {
TestThread4 tickets = new TestThread4();
new Thread(tickets, "小明").start();
new Thread(tickets, "小红").start();
new Thread(tickets, "黄牛").start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在实际运行中,发现居然出现某某抢到了-1张票的情况 ——> 发现问题:多个线程操作同一个对象,线程不安全,数据紊乱
模拟龟兔赛跑:
public class Race implements Runnable {
private static String winner;
public void run(){
for (int i = 0; i <= 100; i++) {
if(Thread.currentThread().getName().equals("兔子") && i >= 80){
try{
Thread.sleep(10);
}catch(Exception e){
e.printStackTrace();
}
}
if(gameOver(i)){
break;
}
System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
}
}
public boolean gameOver(int i){
if(winner != null){
return true;
}
else{
if(i >= 100){
winner = Thread.currentThread().getName();
System.out.println("获胜者是:" + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race r = new Race();
new Thread(r, "兔子").start();
new Thread(r, "乌龟").start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
线程休眠
Thread.sleep()方法:让线程阻塞,传入阻塞时间参数,单位毫秒;抛出异常InterruptedException;sleep时间达到后线程将处于就绪状态;sleep可以模拟网络延时,倒计时等;每个对象都要一个锁,sleep不会释放锁
在游戏中添加一句sleep,充值后去掉这行代码实现“游戏优化”,qtmlgb
线程礼让
Thread.yield()方法:礼让线程,让当前正在执行的线程暂停,但不阻塞;将现场从运行状态转为就绪状态;让cpu重新调度,不一定成功
I/O流基础
流的分类
流:数据从内存和存储设备之间的通道
输入输出流:以流的流向分类
- 输入流:从存储设备到内存
- 输出流:从内存到存储设备
字节字符流:以传输单位划分
- 字节流:以字节为单位,可以读写所要数据
- 字符流:以字符为单位,只能读写文本数据
节点(底层)过滤流:按功能划分
- 节点流:具有实际传输数据的读写功能的流
- 过滤流:在节点流的基础上增强了功能
字节流
InputStream:输入流抽象类
int available();
//关闭流的资源
void close();
//读取下一个字节
abstract int read();
//读取一定量的字节
int read(byte[] b);
int read(byte[] b, int off, int len);
//跳过和丢弃n个字节
long skip(long n);
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
OutputStream:输出流抽象类
void close();
//刷新缓冲
void flush();
//将字节写入磁盘
void write(byte[] b);
void write(int b);
1
2
3
4
5
6
2
3
4
5
6
文件字节流
FileInputStream
class FileInputStream extends InputStream{
//从文件系统中的某个文件中获得输入字节,将读到内容存入b数组,返回实际读到的字节数,如果达到文件的尾部,则返回-1
FileInputStream(String name);
public int read(byte[] b);
}
1
2
3
4
5
2
3
4
5
public class TestFileInputStream {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("E:\\Java\\Java SE\\idea\\src\\resource\\a.txt");
//一次读取一个字节
/*
* int data = 1;
* 一次读取一个字节
* 用循环将文件读完(当读到文件末尾将返回-1)
* while(data != -1){
* data = fis.read();
* System.out.println((char)data);
* }
*/
//一次读取多个字节
byte[] b = new byte[3];
int count;
while((count = fis.read(b)) != -1){
System.out.println(new String(b, 0, count));
}
//关闭输入流
fis.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FileOutputStream
class FileOutputStream extends OutputStream
//一次写入多个字节,将b数组中所有字节写入输出流,存到文件系统的某个文件中
public void write(byte[] b);
}
1
2
3
4
2
3
4
import java.io.FileOutputStream;
public class TestFileOutputStream {
public static void main(String[] args) throws Exception {
//append参数true会使文件不会被覆盖,即每次运行都会继续储存在上次数据之后
//不传入参数或传入false,每次写文件,将会覆盖掉之前数据
FileOutputStream fos = new FileOutputStream("E:\\Java\\Java SE\\idea\\src\\resource\\b.txt", false);
/*
* 一次执行一个字符
* fos.write('a');
* fos.write('9');
* fos.write('s');
*/
String str = "helloWorld\n";
//调用字符串的 getBytes() 方法将字符串转化为字节数组
fos.write(str.getBytes());
fos.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19