多线程简略版
一、概述
继承、实现、线程池。
二、线程声明状态
新建、就绪、运行、阻塞、死亡。
新建状态,new关键字创建线程之后,JVM为其分配内存,并初始化其成员变量的值
就绪状态,start()方法之后。java虚拟机为其创建方法调用栈和程序计数器,等待调度运行
运行状态:就绪状态获得了CPU,开始执行run()方法的线程执行体。
阻塞状态:放弃了CPU使用权,让出了CPU的时间片,暂时停止运行,进入可运行状态。再次获得CPU转到运行状态。
等待阻塞:wait方法,线程进入等待队列。
同步阻塞:同步锁被别的线程占用,JVM会把线程放入锁池中
其他阻塞:sleep、join、I/O请求时JVM会把该线程置为阻塞状态。
线程死亡:
正常结束:run()或call()方法执行完成
异常结束:线程跑出衣蛾Exception或Error
调用Stop:容易造成死锁,不建议使用
终止线程4种方式
正常运行结束、使用退出标志退出线程、Interrupt方法、stop方法
正常运行结束:程序运行结束,线程自动结束
使用退出标志退出线程:伺服线程(奴隶线程)放到while循环当中,需要flag,通过控制flag来控制线程是否结束。
使用了一个 Java 关键字 volatile,这个关键字的目的是使 exit 同步,也就是说在同一时刻只
能由一个线程来修改 exit 的值。
Interrupt:这种方法有两种状态。
一种是线程处于阻塞状态:调用Interrupt()方法时,会抛出异常,要捕获这个异常,使用break跳出循环,才能终止线程
另一种是线程处于非阻塞状态:使用isInterrupted()判断线程的中断标志来退出循环,当使用interrupt方法时,中断标志就会置为true,和使用自定义的标志来控制循环是一样的道理。
stop方法终止线程
这种是线程不安全的,使用stop该线程会释放持有的所有锁,数据就变得不安全了。
Join 等待其他线程终止
join() 方法,等待其他线程终止,在当前线程中调用一个线程的 join() 方法,则当前线程转为阻塞
状态,回到另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待 cpu 的宠幸。
为什么要用 join()方法?
很多情况下,主线程生成并启动了子线程,需要用到子线程返回的结果,也就是需要主线程需要
在子线程结束后再结束,这时候就要用到 join() 方法
线程上下文切换
巧妙地利用了时间片轮转的方式, CPU 给每个任务都服务一定的时间,然后把当前任务的状态保存
下来,在加载下一任务的状态后,继续服务下一任务,任务的状态保存及再加载, 这段过程就叫做
上下文切换。时间片轮转的方式使多个任务在同一颗 CPU 上执行变成了可能。
进程
是指一个程序运行的实例
上下文:
某一时间点CPU寄存器和程序计数器的内容
寄存器:
CPU内部数量较少,但速度很快的内存
程序计数器
是一个专用的寄存器,用于表明指令序列中 CPU 正在执行的位置,存的值为正在执行的指令的位置或者下一个将要被执行的指令的位置,具体依赖于特定的系统。
PCB-切换帧
上下文切换可以认为是内核(操作系统的核心)在 CPU 上对于进程(包括线程)进行切换,上下文切换过程中的信息是保存在进程控制块(PCB, process control block)中的。PCB 还经常被称作“切换桢”(switchframe)。信息会一直保存到 CPU 的内存中,直到他们被再次使用。
4.1.11.6. 上下文切换的活动:
1. 挂起一个进程,将这个进程在 CPU 中的状态(上下文)存储于内存中的某处。
2. 在内存中检索下一个进程的上下文并将其在 CPU 的寄存器中恢复。
3. 跳转到程序计数器所指向的位置(即跳转到进程被中断时的代码行),以恢复该进程在程序
中。
4.1.11.7. 引起线程上下文切换的原因
1. 当前执行任务的时间片用完之后,系统 CPU 正常调度下一个任务;
2. 当前执行任务碰到 IO 阻塞,调度器将此任务挂起,继续下一任务;
3. 多个任务抢占锁资源,当前任务没有抢到锁资源,被调度器挂起,继续下一任务;
4. 用户代码挂起当前任务,让出 CPU 时间;
5. 硬件中断;