前言

  • Java进阶课程的第四篇,线程相关内容。

  • 学习完基础之后就是进阶的内容了。


包含的知识

  1. 多线程的创建:
  • 继承Thread类:通过创建一个继承自Thread类的子类,并重写run()方法来定义线程任务。
  • 实现Runnable接口:通过实现Runnable接口并实现run()方法来定义线程任务,然后将这个对象传递给Thread构造函数。
  • 匿名内部类:在需要的地方直接创建Runnable接口的匿名内部类实例。
  • Lambda表达式:使用lambda表达式简化Runnable接口的实现。
  • 实现Callable接口:与Runnable类似,但Callable可以返回结果,并可能抛出异常。
  1. FutureTask:
  • 包装Callable对象以便它们可以在Thread中执行,并且能够获取执行的结果。
  1. 线程控制方法:
  • start():启动线程,调用线程的run()方法。
  • join():当前线程等待另一个线程完成。
  • sleep(long millis):暂停当前正在执行的线程一段时间。
  1. 线程安全:
  • 演示了如何使用同步方法或同步代码块来确保线程安全,避免多个线程同时访问共享资源时发生数据竞争。
  1. 线程池:
  • 使用ThreadPoolExecutor手动配置线程池。
  • 使用Executors工具类创建预配置好的线程池(如固定大小的线程池)。
  • 提交Runnable或Callable任务到线程池,并处理任务的执行结果。
  1. 并发工具:
  • Future接口用于获取异步计算的结果。
  • TimeUnit枚举用于指定时间单位,比如在线程池配置中的存活时间和阻塞队列的超时时间。
  1. 阻塞队列:
  • 如ArrayBlockingQueue用于存储提交给线程池的任务。
  1. 拒绝策略:
  • 当线程池无法处理新任务时,可以定义拒绝策略,如CallerRunsPolicy。
  1. 锁机制:
  • 代码中注释掉了锁的使用,包括synchronized关键字和ReentrantLock,用于保护临界区代码以保证线程安全。

具体代码

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
package ADV_0;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.*;

public class ADVZc3 {
// main方法本身是由一条主线程负责推荐执行的
public static void main(String[] args) {
System.out.println("======多线程创建方式一:继承Thread类======");
// 创建线程类的对象:代表线程
Thread t1 = new MyThread();
// 调用start方法,启动线程。调用run方法,普通调用。
t1.start(); // 启动线程,让线程执行run方法
for (int i = 0; i < 3; i++) {
System.out.println("Thread主线程输出:" + i);
}

System.out.println("======多线程创建方式二:Runnable接口======");
// 创建线程任务类的对象代表一个线程任务
Runnable r = new MyRunnable();
// 把线程任务对象交给一个线程对象来处理
Thread t2 = new Thread(r); // public Thread(Runnable r)
//Thread t1 = new Thread(r, "1号子线程"); // public Thread(Runnable r,String name)
t2.start();

for (int i = 0; i < 3; i++) {
System.out.println("Runnable主线程输出:" + i);
}

System.out.println("======多线程创建方式二:匿名内部类======");
// 多线程的创建方式二:Runnable接口的匿名内部类来创建
Runnable rr = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Runnable匿名内部类子线程1输出:" + i);
}
}
};
Thread t3 = new Thread(rr); // public Thread(Runnable r)
t3.start();

new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Runnable匿名内部类子线程2输出:" + i);
}
}
}).start();

new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("Runnable匿名内部类子线程3输出:" + i);
}
}).start();

for (int i = 0; i < 3; i++) {
System.out.println("Runnable匿名内部类主线程输出:" + i);
}

System.out.println("======多线程创建方式三:实现Callable接口======");
// 优势:可以获取线程执行完毕后的结果
// Callable接口的实现类对象
Callable<String> c1 = new MyCallable(100);
// 把Callable对象封装成一个真正的线程任务对象FutureTask对象。
/**
* 未来任务对象的作用?
* a、本质是一个Runnable线程任务对象,可以交给Thread线程对象处理。
* b、可以获取线程执行完毕后的结果。
*/
FutureTask<String> f1 = new FutureTask<>(c1); // public FutureTask(Callable<V> callable)
// FutureTask对象作为参数传递给Thread线程对象。
Thread t4 = new Thread(f1);
t4.start();

Callable<String> c2 = new MyCallable(50);
FutureTask<String> f2 = new FutureTask<>(c2); // public FutureTask(Callable<V> callable)
Thread t5 = new Thread(f2);
t5.start();

// 获取线程执行完毕后返回的结果
try {
// 如果主线程发现第一个线程还没有执行完毕,会让出CPU,等第一个线程执行完毕后,才会往下执行!
System.out.println(f1.get());
} catch (Exception e) {
e.printStackTrace();
}
try {
// 如果主线程发现第二个线程还没有执行完毕,会让出CPU,等第一个线程执行完毕后,才会往下执行!
System.out.println(f2.get());
} catch (Exception e) {
e.printStackTrace();
}

System.out.println("======线程的常用方法======");
Thread tc1 = new MyThread0("1号线程");
// tc1.setName("1号线程");
tc1.start();
System.out.println(tc1.getName()); // 线程默认名称是:Thread-索引

Thread tc2 = new MyThread0("2号线程");
tc2.start();
System.out.println(tc2.getName());

// 哪个线程调用这个代码,这个代码就拿到哪个线程
Thread m = Thread.currentThread(); // 主线程
m.setName("主线程");
System.out.println(m.getName()); // main

// 目标:搞清楚线程的join方法:线程插队:让调用这个方法线程先执行完毕
MyThread1 tc3 = new MyThread1();
tc3.start();

for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName() + "线程输出:" + i);
if (i == 1) {
try {
Thread.sleep(1000); // 1000ms = 1s,Thread类的Sleep方法(线程休眠)
tc3.join(); // 插队 让t1线程先执行完毕,然后继续执行主线程
} catch (Exception e) {
e.printStackTrace();
}
}
}

System.out.println("======线程安全======");
// 建小明和小红的共同账户对象,存入10万
Account acc = new Account("ICBC-110", 100000);
// 小明和小红同时去同一个账户取款10万
new DrawThread("小明", acc).start();
new DrawThread("小红", acc).start();


System.out.println("======创建ExecutorService线程池对象0======");
// 1、使用线程池的实现类ThreadPoolExecutor声明七个参数来创建线程池对象。
ExecutorService pool1 = new ThreadPoolExecutor(3, 5,
10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
//核心线程数量:3,最大线程池数量:5,临时线程的存活时间:10,单位s,阻塞队列:ArrayBlockingQueue,
//线程工厂:Executors.defaultThreadFactory(),拒绝策略:new ThreadPoolExecutor.CallerRunsPolicy()

// 2、使用线程池处理任务!看会不会复用线程?
Runnable target = new MyRunnable();
pool1.execute(target); // 提交第1个任务 创建第1个线程 自动启动线程处理这个任务
pool1.execute(target); // 提交第2个任务 创建第2个线程 自动启动线程处理这个任务
pool1.execute(target); // 提交第2个任务 创建第3个线程 自动启动线程处理这个任务
pool1.execute(target);
pool1.execute(target);
pool1.execute(target);
pool1.execute(target); // 到了临时线程的创建时机了
pool1.execute(target); // 到了临时线程的创建时机了
pool1.execute(target); // 到了任务拒绝策略了,忙不过来

// 关闭线程池 :一般不关闭线程池。
// pool.shutdown(); // 等所有任务执行完毕后再关闭线程池!
// pool.shutdownNow(); // 立即关闭,不管任务是否执行完毕!

// 使用线程池处理Callable任务
Future<String> f11 = pool1.submit(new MyCallable(100));
Future<String> f22 = pool1.submit(new MyCallable(200));
Future<String> f33 = pool1.submit(new MyCallable(300));
Future<String> f44 = pool1.submit(new MyCallable(400));
try {
System.out.println(f11.get());
System.out.println(f22.get());
System.out.println(f33.get());
System.out.println(f44.get());
} catch (Exception e) {
e.printStackTrace();
}

System.out.println("======创建ExecutorService线程池对象1======");
// 创建线程池对象
ExecutorService pool2 = new ThreadPoolExecutor(3, 5,
10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

// 2、使用线程池处理Callable任务
Future<String> f111 = pool2.submit(new MyCallable(100));
Future<String> f222 = pool2.submit(new MyCallable(200));
Future<String> f333 = pool2.submit(new MyCallable(300));
Future<String> f444 = pool2.submit(new MyCallable(400));

try {
System.out.println(f111.get());
System.out.println(f222.get());
System.out.println(f333.get());
System.out.println(f444.get());
} catch (Exception e) {
e.printStackTrace();
}

System.out.println("======通过线程池工具类:Executors,调用其静态方法直接得到线程池======");
ExecutorService pool0 = Executors.newFixedThreadPool(3);

Future<String> ff1 = pool0.submit(new MyCallable(100));
Future<String> ff2 = pool0.submit(new MyCallable(200));
Future<String> ff3 = pool0.submit(new MyCallable(300));
Future<String> ff4 = pool0.submit(new MyCallable(400));
try {
System.out.println(ff1.get());
System.out.println(ff2.get());
System.out.println(ff3.get());
System.out.println(ff4.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}

// Thread继承线程
class MyThread extends Thread {
// 重写Thread类的run方法
@Override
public void run() {
// 在run方法中编写线程执行代码
for (int i = 0; i < 3; i++) {
System.out.println("Thread子线程输出:" + i);
}
}
}

//Runnable接口线程
class MyRunnable implements Runnable {
// 重写run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Runnable子线程输出:" + i);
}
}
}

//Callable接口线程
class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
//实现call方法,定义线程执行体
public String call() throws Exception {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return "Callable子线程计算1-" + n + "的和是:" + sum;
}
}

// 定义一个子类继承Thread类,成为一个线程类
class MyThread0 extends Thread {
public MyThread0(String name) {
super(name); // public Thread(String name)
}
@Override
public void run() {
// 在run方法中编写线程的任务代码(线程要干的活儿)
for (int i = 0; i < 3; i++) {
System.out.println(getName() + "子线程输出:" + i);
}
}
}
class MyThread1 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(getName() + "子线程输出:" + i);
}
}
}

class DrawThread extends Thread {
private Account acc; // 记住线程对象要处理的账户对象。

public DrawThread(String name, Account acc) {
super(name);
this.acc = acc;
}

@Override
public void run() {
// 小明 小红 取钱
acc.drawMoney(100000);
}
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Account {
private String cardId; // 卡号
private double money; // 余额
// 小明和小红都到这里来了取钱
//private final Lock lk = new ReentrantLock(); // lock锁:保护锁对象
public void drawMoney(double money) {
//public synchronized void drawMoney(double money) // 同步方法(只改这一个)
String name = Thread.currentThread().getName(); // 拿到当前谁来取钱
//同步代码块Ctrl+Alt+T以下部分
//synchronized (this) {
if (this.money >= money) {// 判断余额是否足够
// 余额足够,取钱
System.out.println(name + "取钱" + money + "元成功!");
// 更新余额
this.money -= money;
System.out.println(name + "余额剩余" + this.money + "元");

} else {
// 余额不足
System.out.println(name + "余额不足");
}
//}
// // lock锁
// lk.lock(); // 上锁
// try {
// if (this.money >= money) {
// System.out.println(name + "取钱成功,吐出了" + money + "元成功!");
// this.money -= money;
// System.out.println(name + "取钱成功,取钱后,余额剩余" + this.money + "元");
// } else {
// System.out.println(name + "取钱失败,余额不足");
// }
// } finally {
// lk.unlock();// 解锁
// }
}
}