Java多线程详解
2021-07-06 0点20分
02、线程,进程,程序
-
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
-
进程是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
-
通常在一个进程中可以包含若干个线程,当然一个进程至少有一个线程,不然没有存在的意义。
**很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核。
本章核心概念
1.线程是独立的执行路径;
2.在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
3.main()称之为主线程,为系统的入口,用于执行整个程序;
4.在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预;
5.对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
6.线程会带来额外的开销,如CPU调度时间,并发控制开销;
7.每个线程在自己的工作内存教会,内存控制不当会造成数据不一致。
02 线程创建
1.线程三种创建方式
- 继承Thread类(重点)
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
- 不建议使用:避免OOP单继承局限性
- 实现Runnable接口(重点)
- 重写run()方法,
- 启动线程:传入目标对象 + Thread对象.start()
new Thread(对象名).start()
- 推荐使用:避免单继承局限性,方便同一个对象被多个线程使用
- 实现Callable接口(了解)
- 实现Callable接口,需要返回值
- 重写call()方法,需要抛出异常
- 创建目标对象
- 创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(3);
- 提交执行:
Future<Boolean> result1 = ser.submit(t1);
- 获取结果:
boolean r1 = result1.get()
- 关闭服务:
ser.shutdownNow()
2.普通方法和调用多线程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXK2jZc0-1632666167100)(Java多线程详解.assets\image-20210706092000318.png)]
3.commons.io
4.总结 线程开启不一定立即执行,由CPU调度
03 静态代理
1.总结
- 真实 对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色(代理对象拥有真实对象的类实例)
04 Lambda表达式
1.理解函数式接口
- ==定义:==任何接口,如果只包含唯一一个抽象方法,那么它就是一个 函数式接口;
- 对于函数式接口,我们可以通过Lambda表达式来创建该接口的对象。
2.静态内部类,局部内部类、匿名内部类、
- 局部内部类:在方法内部的类
- 匿名内部类:没有类的接口,必须借助接口或者父类。(接口不能被实例化啊,这里又是怎么回事?)
ilike = new Ilike() {
@Override
public void lambda() {
System.out.println("匿名内部类\t"+getClass().getName());
}
};
注意,表达式结尾的“;”,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SOm3lDsE-1632666167102)(Java多线程详解.assets/image-20210713143856890.png)]
3.Lambda表达式:
接口 = (参数) -> {方法体};
1.去掉返回值
去掉括号
去调花括号
总结:
lambda表达式只能有一行的代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹。前提是接口为函数式接口;
多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号。
05 线程状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oRcAfGEL-1632666167102)(Java多线程详解.assets/image-20210710154844132.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vu9uUGcj-1632666167103)(Java多线程详解.assets/image-20210710155059743.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pD3q8DA3-1632666167104)(Java多线程详解.assets/image-20210710155335670.png)]
06 线程停止
1.建议线程正常停止:利用次数,不建议死循环;
2.建议使用标志位:设置一个标志位;
3.不要使用stop或者destroy等过时或者JDK不建议使用的方法。
07 线程休眠
- 每一个对象都有一个锁,sleep不会释放锁。
- 模拟网络延时:放大问题的发生性
- 模拟得倒计时
08 线程礼让
- 让当前正在执行的线程暂停,但不阻塞
- 将线程从运行转为就绪状态
- 让CPU重新调度,==礼让不一定成功!==看CPU心情。
09 线程强制执行
-
Join合并线程,待此线程执行完毕后,再执行其他线程,其他线程阻塞(类似插队)
-
此方法阻塞,这是主动让别人插队?
1.死亡之后的线程是不能再次启动的-------线程不能启动两次。
10 线程的优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
- 线程优先级低只是意味着调度的概率低,而不是优先级低就不会被调用了,这都是看CPU的调度
- 优先级的设定建议在start()调度前
11 守护线程
1.线程分为==用户线程和守护线程;==
2.虚拟机必须确保用户线程执行完毕 ;
3.虚拟机不用等待守护线程执行完毕;
4.如:后台记录操作日志、监控内存、垃圾回收
thread.setDaemon(true);
默认是false表示是用户线程,正常的线程都是用户线程……
12 线程同步机制
- 并发:多个线程访问同一个对象,并且某些线程还想修改这个对象------>线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。
1.队列和锁
- 线程同步形成条件:**队列 **+ 锁------>解决安全性
2.由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized
,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。
- 一个线程持有锁会导致其他所有需要此锁的线程挂起;
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时引起性能问题;
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
3.每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
4.super只能写在构造方法第一行。
13 同步方法和同步块
- 静态方法的锁是所在类的Class对象,普通方法的锁是this对象。
13.1.同步方法
1.由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:
-
同步方法:
public synchronized void method(int args){}
synchronized方法控制对 对象 的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行。
-
缺点:容将一个大的方法申明为synchronized将会影响效率。
13.2 同步块
1.同步块:synchronized(Obj){}
2.Obj称之为同步监视器
- Obj可以是任何对象,但是推荐使用共享资源作为同步监视器;
- 同步方法中无需指定同步监视器,因为同步方法中的同步监视器就是this,就是这个对象本身,或者是class(反射)。
3