目录
认识线程
概念:
一个线程就是一个 "执行流". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 "同时" 执行着多份代码.
为啥要有线程:
首先, "并发编程" 成为 "刚需”;
- 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU资源。
- 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程。
其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量;
进程:系统分配资源的最小单位,系统以时间片轮转调度的方式来执行
时间片轮转调度:并发的,需要保存上下文信息简单说线程就是轻量级进程:相比进程,创建、调度、销毁效率要高很多。
进程和线程的区别:
- 进程是包含线程的. 每个进程至少有一个线程存在,即主线程。
- 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间。
- 进程是系统分配资源的最小单位,线程是系统调度的最小单位。(系统调度:由CPU执行进程的代码指令)
- 线程的创建,销毁代价比进程小(状态转换进程的开销大于线程)
- 线程有bug可能会造成整个进程挂掉;进程间是独立运行的。
多线程的优势和缺陷:
优势:
- 充分利用CPU资源,提高执行效率
- io等阻塞时能同时接收输入
缺陷:
- 线程的创建/销毁也是具有一定的系统开销(一般用于执行耗时比较长的任务)
- 会增加编码的复杂程度
线程的状态:
new:创建态
runnable:可运行态
waiting:等待
timed_waiting:超时等待
blocked:阻塞
terminated:终止
Thread:线程
创建线程:
(1)继承Thread(要重写run方法,定义要执行的任务代码)
class MyThread extends Thread{
@Override
public void run() {
System.out.println("这里是线程运行的代码");
}
}
继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
(2)实现Runnable接口(也要重写run方法)
public static void main(String[] args) {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("这里是线程执行的代码");
}
});
t.start();//线程启动
}
实现 Runnable 接口, this 表示的是 MyRunnable 的引用 需要使用 Thread.currentThread()
(3)实现Callable接口
(获取线程执行的结果)
线程的启动
thread.start() => 申请系统调度,执行thread中的任务(重写run方法)
thread中,start() 和 run() 有什么区别?
start:启动线程的方式
run:属于线程任务的描述
多个线程,同时存在并发并行的情况
并发:一个CPU以时间片轮转调度的方式,执行多个线程,给我们感觉像在同时执行
并行:多个CPU在一个时间点,同时执行多个进程
线程的属性:
public static void main(String[] args) {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类run");
}
},"线程名");
t.start();
System.out.println(t.getName());//获取线程名称
System.out.println(t.getState());//获取线程状态
System.out.println(t.isDaemon());//是否是后台线程
System.out.println(t.isAlive());//是否存活:如果启动后销毁前,都是存活
}
关于后台线程只要记住:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
是否存活,即简单的理解,为 run 方法是否运行结束了。
Thread中常用的方法:
join()等待
当前线程(t.join在哪个线程执行,就是谁)等待,直到线程引用执行完毕
当前线程等待,最多等待给定的毫秒数,或者线程引用对象死亡
使用join后,两个线程从并发并行随机执行的方式,就变为有一定顺序
currentThread() 当前线程
返回当前线程的引用对象
sleep()休眠
让当前线程休眠(超时等待)给定时间
线程的中断
使用注意事项:
- java中断,是以中断标志位的方式来执行
- interrupt是发起中断的动作,但线程是否中断,由自己代码是否判断标志位来决定
- 线程如果处于某些等待/超时等待(会抛InterruptedException),是允许的
中断的方式是:抛异常来中断,抛异常以后,会重置/还原中断标志位
如果线程因为调用wait、join、sleep 等方法而挂起,则以InterruptedException异常的形式通知,清除中断标志,当出现这个异常的时候,要不要结束线程取决于catch中代码的写法,可以选择忽略这个异常,也可以跳出循环结束线程。Thread.interrupted() 判断当前线程的中断标志被设定,清除中断标志;Thread.currentThread().isInterrupted()判断指定线程的中断标志被设置,不清除中断标志,这种方式通知收到的更及时,即使线程正在sleep也可以马上收到。