初识多线程
内容纲要
实现多线程的方式
官方
- 方法一: 实现Runnable类 (更好)
- 从解构角度,创建线程和线程的具体逻辑应该分离实现解耦
- 使用继承的方式,我们每次需要创建一个线程就需要一个独立的类独立的线程,独立的线程消耗会很大,Runnable可是使用线程池
- 方法二:继承Thread类
- 两种方法的本质对比
- 方法一:最终调用target.run(),target也就是新创建实现Runnable的类
- 方法二:run()整个都被重写
同时使用两种方法会怎么样?
public class BothRunnableThread {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我来自Runnable");
}
}) {
@Override
public void run() {
System.out.println("我来自Thread");
}
}.start();
}
}
最终会执行Thread中的方法,原因:实现Runnable后,在run方法中则会去调用Runnable对象的run方法,但是紧接着继承Thread类,再重写run方法,所以run方法中原本调用Runnable对象的run方法的语句就被覆盖了,Runnable的run得不到运行,所以执行的是Thread中的方法
最精准的描述
- 通常我们可以分为两类, Oracle也是这么说的
- 准确的讲,创建线程只有一-种方式那就是构造Thread类,而实现线程的执行单元有两种方式
- 方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类
- 方法二:重写Thread的run方法(继承Thread类)
启动线程的正确和错误方式
start()和run()的比较
- run() run方法还是在主线程
- start() 真正开启了一个子线程
start()方法含义
- 启动新线程,通知jvm在空闲的时候运行新线程
- 准备工作:让自己处于就绪状态(已经获取到除CPU以外的所有资源,比如设置了上下文,栈,线程状态,寄存器)
- 不能重复调用start()
停止线程
如何停止线程?
- 原理介绍:使用interrupt来通知,而不是强制
特殊方法
- static boolean interrupted() 返回之后会将中断状态设置为false
- boolean isInterrupted() 返回中断状态后不会清除状态
- Thread.interrupted()的目标对象
响应中断的方法总结
Thread 与 Object
方法概览
wait、notify、notifyAll作用、用法
阻塞阶段
另-个线程调用这个对象的notify()方法且刚好被唤醒的是本线程;
另一个线程调用这个对象的notifyAll()方法;
过了wait(long timeout)规定的超时时间,如果传入0就是永久等待;
线程自身调用了interrupt()
- notify、notifyAll 都需要在
synchronized
关键字保护下运行,如果在外面则会抛出异常
join
在每个线程运行结束后,都会去调用notify_all进行wait的唤醒
even.join();
# join的等价代码
synchronized (even){
even.wait();
}
yield 方法详解
释放自身的CPU时间片,状态依然是Runable状态,原因:yield释放的时间片并会释放自己的锁,不会陷入阻塞,下一次CPU调度随时都可能调度起来
线程个属性纵览
线程各属性总结
线程的未捕获异常UncaughtException应该如何处理?
为什么需要UncaughtExceptionHandler ?
- 主线程可以轻松发现异常,子线程却不行
- 子线程异常无法用传统方法捕获
- 不能直接捕获的后果,提高健壮性
解决方案
- 方案一(不推荐 ) :手动在每个run方法里进行try catch
- 方案二(推荐) :利用UncaughtExceptionHandler
- UncaughtExceptionHandler接口
- void uncaughtException(Thread t, Throwable e);
异常处理器的调用策略
自己实现
-
给程序统一设置
-
给每个线程单独设置
-
给线程池设置
volatile 解决可见性
每次更新了之后,volatile都会强制将线程内存刷新回主内存中
共有 0 条评论