Java多线程-1

线程创建

继承Thread类

实现Runnable接口

实现Callable接口 (了解)

一、继承Thread类

  1. 自定义线程类继承Thread类
  2. 重写run() 方法, 编写线程执行体
  3. 创建线程对象, 调用start() 方法启动线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 创建线程方式一: 继承Thread类, 重写run() 方法, 调用start开启线程
public class TestThread1 extends Thread {
@Override
public void run() {
// run 方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码" + i);
}
}

// main 线程 主线程
public static void main(String[] args) {
// 创建一个线程对象
TestThread1 testThread1 = new TestThread1();
// 调用start方法开启线程
testThread1.start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程" + i);
}
}
}

实现简单下载器

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
// 练习Thread , 实现多线程同步下载图片
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 webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载了文件名为: " + name);
}

// 主线程
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://z3.ax1x.com/2021/10/15/58F85q.jpg", "image/dog1.jpg");
TestThread2 t2 = new TestThread2("https://z3.ax1x.com/2021/10/15/58F85q.jpg", "image/dog2.jpg");
TestThread2 t3 = new TestThread2("https://z3.ax1x.com/2021/10/15/58F85q.jpg", "image/dog3.jpg");

t1.start();
t2.start();
t3.start();
}
}


// 下载器
class WebDownloader {
// 下载方法
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}

二、实现Runnable接口

  1. 定义MyRunnable类实现Runnable接口
  2. 实现run() 方法, 编写线程执行体
  3. 创建线程对象, 调用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
/ 创建线程方式2: 实现runnable接口, 重写run方法, 执行线程须丢入runnable接口实现类。调用start方法
public class TestThread3 implements Runnable{
@Override
public void run() {
// run 方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码" + i);
}
}

public static void main(String[] args) {
// 创建runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();

// 创建线程对象, 通过线程对象来开启我们的线程 -- 代理
// Thread thread = new Thread(testThread3);
// thread.start();

// 简写
new Thread(testThread3).start();

for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程" + i);
}
}
}

三、实现Callable接口

    1. 实现callable接口, 需要返回值类型
    1. 重写call方法, 需要抛出异常
    1. 创建目标对象
    4. 创建执行服务  ExecutorService ser = Executors.newFixedThreadPool(3);
    1. 提交执行 Future<Boolean> r1 = ser.submit(t1);
    1. 获取结果  Boolean rs1 = r1.get();
    1. 关闭服务  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
54
55
56
// Callable好处: 可以定义返回值, 可以抛出异常   
// 线程创建方式三: 实现callable接口 <Boolean>是返回值
public class TestCallable implements Callable<Boolean> {

private String url; // 网络图片地址
private String name; // 保存文件名

public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}

@Override
public Boolean call() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载了文件名为: " + name);
return true;
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://z3.ax1x.com/2021/10/15/58F85q.jpg", "image/dog1.jpg");
TestCallable t2 = new TestCallable("https://z3.ax1x.com/2021/10/15/58F85q.jpg", "image/dog2.jpg");
TestCallable t3 = new TestCallable("https://z3.ax1x.com/2021/10/15/58F85q.jpg", "image/dog3.jpg");

// 创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
// 获取结果
Boolean rs1 = r1.get();
Boolean rs2 = r2.get();
Boolean rs3 = r3.get();

System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
// 关闭服务
ser.shutdownNow();

}
}
// 下载器
class WebDownloader {
// 下载方法
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}

小结

不建议使用 继承Thread类 : 避免OOP单继承局限性

推荐使用 实现Runnable接口 : 方便灵活,方便同一个对象被多个线程使用

Callable好处: 可以定义返回值, 可以抛出异常

线程不安全

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
// 多个线程同时操作同一个对象
// 买火车票的例子
// 发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class TestThread4 implements Runnable {

// 票数
private int ticketNums = 10;

@Override
public void run() {
while (true) {
if (ticketNums <= 0)
break;
// 模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "票");
}
}

public static void main(String[] args) {
TestThread4 ticket = new TestThread4();

new Thread(ticket, "小明").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "黄牛党").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
41
42
43
44
45
46
47
// 模拟龟兔赛跑
public class Race implements Runnable {
// 胜利者
private static String winner;

@Override
public void run() {
for (int i = 0; i <= 100; i++) {
// 模拟兔子休息
if (Thread.currentThread().getName().equals("兔子") && i%10 ==0){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

// 判断比赛是否结束
boolean flag = gameOver(i);
if (flag)
break;

System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步");
}
}

// 判断是否完成比赛
private boolean gameOver(int steps) {
// 判断是否有胜利者
if (winner != null) {//已经存在
return true;
}
if(steps >= 100){
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
return false;
}

public static void main(String[] args) {
Race race = new Race();

new Thread(race,"兔子").start();
new Thread(race,"乌龟").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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 静态代理模式总结
// 真实对象和代理对象都要实现同一个接口
// 代理对象要代理真实角色

// 好处:
// 代理对象可以做很多真实对象做不了的事
// 真实对象专注做自己的事
public class StaticProxy {
public static void main(String[] args) {

new Thread(()-> System.out.println("I LOVE YOU")).start();

You you = new You(); // 你要结婚
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.HappyMarry();
}
}

interface Marry {
void HappyMarry();

}

// 真实角色, 你去结婚
class You implements Marry {

@Override
public void HappyMarry() {
System.out.println("老师要结婚了,超开心");
}
}

// 代理角色,帮助你结婚
class WeddingCompany implements Marry {
// 代理真实目标角色
private Marry target;

public WeddingCompany(Marry target){
this.target = target;
}

@Override
public void HappyMarry() {
before();
this.target.HappyMarry(); // 真实对象
after();
}

private void after() {
System.out.println("结婚之后, 收尾款");
}

private void before() {
System.out.println("结婚之前,布置现场");
}
}

Lamda表达式

避免匿名内部类定义过多

实质属于函数式编程的概念

(params) -> expression[表达式]

(params) ->statement[语句]

(params) -> {statements}

避免匿名内部类定义过多

只留下核心代码

函数式接口:

任何接口,如果只包含唯一一个抽象方法,那他就是一个函数式接口

1
2
3
public interface Runnable{
public abstract void run();
}

对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

1
2
3
4
5
6
7
8
9
10
11
12
public class TestLambda2 {
public static void main(String[] args) {
Ilove love = (int a) -> {
System.out.println("I love you --> " + a);
};
love.love(520);
}
}

interface Ilove{
void love(int a);
}

简化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 简化1. 参数类型
Ilove love = (a)->{
System.out.println("I love you --> " + a);
};
love.love(520);

// 简化2. 简化括号
Ilove love = a->{
System.out.println("I love you --> " + a);
};
love.love(520);

// 简化3. 去掉花括号 (代码只有一行可简化)
Ilove love = a-> System.out.println("I love you --> " + a);
love.love(520);

总结

lambda表达式只有一行代码的情况下才能简化成为1行,如果有多行,用 {} 包裹

前提是: 接口为函数式接口