多线程安全基础(三)
进程
通常在一个进程中可以包含若干线程,当然一个进程中至少有一个线程,不然没有存在的意义。
注意:
- 很多多线程是模拟出来的,真正的多线程是指多个cpu,既多核,如服务器。
如果是模拟出来的多线程,既在一个cpu的情况下,在同一时间点,cpu
只能执行一个代码,因为切换的很快,所以就有同时执行的错觉 - 线程就是独立的执行路径
- 在程序运行时,及时没有自己创建的线程,后台也会有多个线程,如主线程
自定义线程流程
- 自定义线程类继承Thread
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
实现Runnable接口
- 第一步:定义MyRunnable类实现Runnable接口
- 第二步:实现run方法,编写线程执行体
- 创建线程对象,调用start方法启动线程
- 推荐使用Runnable接口,java是单继承
小结:
- 继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start();
- 不建议使用:避免OOP单继承局限性
- 实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 避免了单继承,方便灵活,方便同一个对象被多个线程使用;
runnable
callable两种方式
静态代理模式
- 真是对象和代理对象都要实现同一接口
- 代理对象要代理真实角色
静态代理的优点:
- 代理对象可以做很多真实对象做不了的事情
- 真实对象就专注做自己的事情
Lambda表达式
优点:避免内部类定义过多
其实质属于函数式编程的概念
- (params) -> expression(表达式)
- (params) -> statement[语句]
- (params) ->
比如:
new Thread (()-> System.out.println("多线程学习...")).start();
Lambda
总结:
lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那就用代码块包裹;
前提是接口为函数式接口,函数式接口只有一个抽象方法;
多个参数也可以去掉参数类型,要去掉就都去掉;必须加上括号,
线程的五种状态
- 创建状态
- 就绪状态
- 阻塞状态
- 运行状态
- 死亡状态
- 创建线程(启动线程)-》就绪状态
- 阻塞状态(阻塞解除)-》就绪状态
- 运行状态(释放CPU资源)-》就绪状态
- 就绪状态(获得CPU资源)-》运行状态
- 运行状态(等待用户输入,线程休眠等)-》阻塞
- 运行状态(线程自然执行完毕外部干涉终止线程)-》死亡状态
详细
- new的时候就会创建一个线程
- 调用start方法就进入了就绪状态
- 调度,就开始运行状态
- 当调用sleep或者wait哦同步锁定时,
- 死亡状态
线程的方法有
- setPriority(int newPriority)更改线程的优先级
- static void sleep(long millis)指定毫秒数内让当前正在执行的线程休眠
- void join() 等待该线程终止
- static void yield() 暂停当前正在执行的线程对象,并执行其他线程
- void interrupt() 中断线程,别用这个方式
- boolean isAlive() 测试线程是否处于活动状态
线程休眠
- sleep存在异常InterruptedException;
- sleep时间达到后线程进入就绪状态;
- sleep可模拟网络延时,倒计时等
- 每一个对象都有锁,sleep不会释放锁;
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功!看cpu心情
- 礼让不一定成功
join
- join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
- 可以想成插队
线程的优先级
- java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行;
- 线程的优先级用数字表示,范围从1-10
- Thread.MIN_PRIORITY = 1;
- Thread.MAX_PRIORITY = 10;
- Thread.NORM_PRIORITY = 5;
- 使用以下方式改变或获取优先级
- getPriority().setPriority(int xxx)
守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完成
- 如,后台记录操作日志,监控内存,垃圾回收等待...
线程同步机制
并发:
同一个对象被多个线程同时操作;
处理多线程问题时,多个线程;
答:进行线程同步,需要有对象的等待池;
队列和锁
线程同步需要队列和锁;
解决安全性;
- 由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为保证数据在方法
中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,
使用后释放锁,- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多进程竞争下,枷锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题;