Java 多线程基础
P1 多线程概述
学习路线:
- 线程简介
- 线程实现(重点)
- 线程状态
- 线程同步(重点)
- 线程通信问题
- 高级主题
线程、进程、多线程
核心概念:
- 多任务:看起来多任务,其实同一时间,其实依旧只在做一件事
- 多线程:多个方法同时执行;
- 进程:一个程序开启一个进程,一个进程包含多个线程;
main()
称为主线程,为系统的入口,用于执行整个程序;- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致;
Process(进程)与Thread(线程):
- 程序:是一个静态概念,包含指令和数据的有序集合。
- 进程:程序执行的一次过程,是一个动态概念。进程是系统资源分配的单位。
- 线程:字啊一个进程中,通常包含若干线程,一个进程至少有一个线程。线程是CPU调度和执行的单位。
注意:很多多线程是模拟出来的,真正的多线程是指多个CPU即多核。如果是模拟出来的多线程,在一个CPU的情况下,在同一个时间点上,CPU只能执行一个代码,因为切换的很快,才有同时执行的错觉。
P3 线程创建
Thread、Ruunnable、Callable:
- 继承
Thread
类,重点 - 实现
Runnable
接口,重点 - 实现
Callable
接口,了解
P3 Thread
- 自定义线程类,继承Thread类;
- 重写
run()
方法,编写线程执行体; - 创建线程对象,调用该对象的
start()
方法启动线程;注意,不是调用run()
方法!
注意点:
start()
方法启动多线程,线程不一定立即执行,CPU 安排调度;
P4 Runnable
- 自定义线程实现类 P,实现
Runnable
接口; - 实现
run()
方法,编写线程执行体; - 创建线程实现类的对象 p;
- 对象
p
传入Thread
类的构造器中,创建线程对象 r,调用对象 r 的start()
方法启动线程;new Thread(p).start();
小结:
- 推荐使用 Runnable 对象,因为 Java 单继承的局限性!
P6 初始并发问题
Thread.currentThread().getName()
获取当前线程的名字;Thread.sleep(2000)
延时 2 秒;
多个线程操作同一个资源的情况下,数据紊乱(多人买到了同一个票)
P7 龟兔赛跑
Race race = new Race();
new Thread(race, "兔子").start();
new Thread(race, "乌龟").start();
P8 Callable
- 实现 Callable 接口,需要返回值类型;
- 重写
call
方法,需要抛出异常; - 用接口实现类创建目标对象
t1
; - 创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行:
Future<Boolean> result1 = ser.submit(t1);
- 获取结果:
boolean r1 = result.get()
- 关闭服务:
ser.shutdownNow();
P9 静态代理
婚庆公司作为代理的例子。
总结:
- 定义一个接口,里面定义了真实对象应该具有的方法名;
- 真实对象的类去实现接口类;
- 代理类也需要去实现接口类:
- 定义一个接受真实对象的构造器
- 重写接口方法,方法内做一些
before/after
操作的同时,调用一些传入的真实对象的同名方法;
通俗来讲:代理去执行一个动作,看着像是代理做的,其实,他帮你做了一些事情的同时,还是真实的你去执行了某些方法!因为是真实的对象传进代理作为构造器的入参了!
为何在这里要介绍静态代理?因为线程接口底层应用的和它相似!
new Thread(()->System.out.println("test")).start();
new WeddingCompanny(new You()).marry();
P10 Lambda 表达式
- 其实质属于函数式编程的概念,理解 Functional Interface(函数式接口)是学习 Java8 lambda 表达式的关键所在;
- 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口!
- 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象!
为什么要使用 lambda 表达式?
- 避免匿名内部类定义过多;
- 使代码更简洁;
- 去掉没有意义的代码,只留下核心的逻辑;
总结:
- lambda 表达式去掉方法体的花括号的前提是,只有一行代码,否则不能简化掉花括号;
- 前提是接口是函数式接口!
IPerson
是一个函数式接口,其中仅有一个方法; - 多个参数也可以去掉参数类型;
IPerson p = (name,age)->System.out.println("name: "+name+" age: ":+age);
——可以看到,->
后面就是接口中定义的方法的重写!
P11 线程停止
线程有 5 大状态,待补充截图
- 新生状态 new
- 就绪状态 start
- 阻塞状态
- 运行状态
- dead状态
线程方法:
- setPriority()
- sleep()
- join()
- yield()
- interrupt() 不建议使用
- isAlive()