线程(特性/生命周期/创建方式)
2021-02-25 21:49:36 1 举报
AI智能生成
线程(特性/生命周期/创建方式)
作者其他创作
大纲/内容
进程和线程
进程是操作系统分配资源的基本单位
线程是操作系统执行调度的基本单位
线程切换
CPU
ALU:计算单元
Registers:寄存器组(用来存数据)
PC:程序计数器
由操作系统的线程调度器控制
平分时间片算法
CFS算法
线程的特性
可见性
存在问题
数据的读取顺序:从内存读到 L3 → L2 → L1 → 寄存器后,ALU开始做计算
再次读取数据时,寄存器优先从 L1(本地缓存)中读取,不会重新从内存中读
解决
lock前缀指令
触发总线锁,独占对象的使用,保证对象被修改时刷新到主内存
触发MESI协议,使其他CPU缓存中该变量的副本失效
volatile
happen-before原则
MESI缓存一致性协议
有序性
存在问题
指令重排(单线程没有影响)
多线程下会有问题,比如DLC单例会取到半成品
解决
内存屏障
禁止指令重排序
原子性
存在问题
多线程下线程安全问题
比如100个线程做累加,最后结果小于预期值
解决
加锁
生命周期
6 种状态
新建(NEW)
使用 new 关键字创建线程后进入新建状态,此时还没有调用 start()
可运行(RUNNABLE)
线程调用 start() 方法后,进入就绪状态,等待CPU分配时间片
操作系统中就绪(READY)和运行中(RUNNING)两种状态的统称
操作系统中就绪(READY)和运行中(RUNNING)两种状态的统称
阻塞(BLOCKED)
当进入 synchronized 同步代码块或同步方法时,且没有获取到锁,
线程就进入了 blocked,直到锁被释放,重新进入 runnable 状态
线程就进入了 blocked,直到锁被释放,重新进入 runnable 状态
等待(WAITING)
当线程调用 wait() 或者 join() 时,会进入到 waiting 状态,当调用
notify 或 notifyAll 时,或者 join 的线程执行结束后会进入 runnable
notify 或 notifyAll 时,或者 join 的线程执行结束后会进入 runnable
超时等待(TIMED_WAITING)
当线程调用 sleep (time)或者 wait (time)时,进入 timed waiting 状态
当休眠时间结束后,或者调用 notify 或 notifyAll 时会重新进入 runnable
当休眠时间结束后,或者调用 notify 或 notifyAll 时会重新进入 runnable
终止(TERMINATED)
程序执行结束,线程进入 terminated 状态
扩展
java.lang.Thread类的源码中有个内部枚举,这里可以看到6种状态
创建线程的方式
继承Thread类
重写run方法,没有返回值
new MyThread().start();
缺点:Java是单继承,如果继承Thread就不能继承其他类
代码实现
实现Runnable接口
重写run方法,没有返回值
在创建Thread对象时传进去
优点:不受单继承的限制
代码实现
实现Callable接口
重写call方法,有返回值,可抛异常
需要用FutureTask在外部封装一下再传递给
Thread,FutureTask就是Runnable的实现类
Thread,FutureTask就是Runnable的实现类
FutureTask使用场景?
两件事或多件事同时完成
FutureTask两个个构造函数?
第一个构造函数要求传入Callable对象
第二个构造函数要求传入Runnable对象和返回值类型
FutureTask的其他方法?
get()
会一直等待子线程运行结束
get(5,TimeUnit.SECOND)
传入等待时间,超时后会抛出TimeoutException异常,需要捕捉处理
isDone()
询问子线程是否执行完成,返回值是boolean
优点
在主线程中可获取到子线程的返回值
直接调用FutureTask对象的get()方法
为什么可以获取到?如何获取的?
在主线程中可获取到子线程发生的异常
通过getCause()方法获取子线程的异常
代码实现
使用线程池(单独重点介绍)
终止/退出线程的方式
run() 方法运行结束
如何判断线程是否停止
this.interrupted()
返回线程状态并停止线程
this.isInterrupted()
不具备清除状态功能
使用退出标志位/共享变量
定义全局变量,使用volatile修饰的boolean退出标志位来控制循环
调用interrupt()方法终止线程
区分阻塞和和非阻塞两种情况
区分阻塞和和非阻塞两种情况
调用interrupt()时会抛出异常,处于阻塞状态中的线程可捕获interruptedException异常,通过break跳出循环
调用interrupt()时会调用interrupted()函数,未阻塞的线程可使用isInterrupted()判断线程的中断标志来退出循环
守护线程
在java线程中有两种线程,一种是用户线程,一种是守护线程,典型得守护线程就是垃圾回收线程
守护线程是一种特殊得线程,当进程中不存在用户线程(非守护线程)时,守护线程自动销毁
可以使用setDaemon()设置线程为守护线程,注意不能把一个正在运行的线程设置为守护线程
所以,setDaemon()方法必须在start()方法前面,并且守护线程中产生得线程也是守护线程
所以,setDaemon()方法必须在start()方法前面,并且守护线程中产生得线程也是守护线程
常见问题
谈谈你对线程安全的理解?
当多个线程访问一个对象时,如果不进行额外的同步控制或其他的协调操作,
调用这个对象的行为都可以获得正确的结果,我们就说这个对象是线程安全的
调用这个对象的行为都可以获得正确的结果,我们就说这个对象是线程安全的
什么时候考虑线程安全?
多个线程访问同一个资源
资源是有状态的,比如字符串拼接
如何做到线程安全?
使用Synchronized关键字给代码块或者方法加锁
比如StringBuffer的源码中,方法上均添加了Synchronized
sleep()与wait()的区别?
sleep() 属于Thread类,wait() 属于Object类
sleep() 不会释放对象锁,wait() 会释放对象锁
sleep() 必须指定时间,wait() 可指定也可以不指定
sleep() 可以使用在任何代码块,wait() 必须在同步方法或同步代码块中使用
思考:为什么wait要定义在Object中而不定义在Thread中?
Java的锁是对象级别的,不是线程级别的
sleep() 休眠指的就是线程休眠,所以在Thread类
思考:为什么wait必须写在同步代码块中?
避免 CPU 切换到其他程,而其他线程又提前执行了 notify 方法,那这样就
达不到我们的预期(先 wait 再由其他程来唤醒),所以需要一个同步锁来保护
达不到我们的预期(先 wait 再由其他程来唤醒),所以需要一个同步锁来保护
notify()和notifyAll()有什么区别?
使用notifyall可以唤醒所有处于wait状态的线程,使其重新进入锁的争夺队列中,
而notify只能唤醒一个,推荐使用notifyall
而notify只能唤醒一个,推荐使用notifyall
interrupted()和isInterrupted()的区别?
interrupted查询当前线程的中断状态,并且清除原状态
isInterrupted仅仅是查询当前线程的中断状态
Java中用到的线程调度算法是什么?
有两种调度模型:分时调度模型和抢占式调度模型
分时调度模型是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程
占用的CPU的时间片这个也比较好理解
占用的CPU的时间片这个也比较好理解
java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU
如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU
如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU
0 条评论
下一页
为你推荐
查看更多