多线程
进程与线程
进程
是什么?
在linux是用命令ps aux
可以查看正在运行的进程
进程是程序的一次执行过程。在Java中,每次程序运行至少启动两个线程,一个main线程,一个是垃圾收集进程。在比如打开一个浏览器时,系统会创建一个进程,打开一个新的标签页相当于创建了一个线程,一个进程可以拥有多个线程。
线程
是什么?
线程是比进程更小的执行单位,一个进程包含多个线程,线程处理细小的任务,线程被称为是轻量级的线程。
线程生命周期
新建
new关键字创建了一个线程之后,线程就处于新建状态,
jvm为线程分配内存,初始化成员变量值。
就绪
当线程对象调用了start() 方法之后,该线程就处于就绪状态,就绪态的线程只缺少处理器资源就可以运行了;
jvm为线程创建方法栈和程序技术器,等待线程调度器调度。
运行
就绪态的线程获得cpu资源,开始运行run(),此时线程进入运行状态。
阻塞
线程可以主动或被动放弃cpu资源,进入阻塞状态
- 线程调用sleep()主动放弃cpu
- 线程等待io
- 线程尝试获得一个同步锁,但是该同步锁被其他线程所占用
- 线程在等待某个通知(notify)
- 程序调用了线程的suspend()方法将线程挂起。该方法容易导致死锁。
死亡
线程可能有以下3种方式死亡
- run()或call()方法执行完成,线程正常结束。
- 线程执行出错,抛出异常
- 调用stop()方法结束线程,该方法容易产生死锁。
线程安全
若一个程序使用了多个线程,它每次的运行结果和只使用单线程的结果是一样的,那么就称为线程安全的。若一个程序中对同一个变量有读和写两个线程,因为我们不知道这两个线程的执行顺序,先执行读和先执行写的两个进程读出来的结果是不一样的,这就是线程不安全。
例子:多个窗口进行售票,在程序中一个窗口绑定一个线程运行,若没有适当的线程同步措施,可能会造成一张票被售出多次的情况。
线程的同步方法
- 同步代码块(synchronized)
- 同步方法(synchronized)
- 同步锁(ReenreantLock)
- 特殊域变量(volatile)
- 局部变量(ThreadLocal)
- 阻塞队列(LinkedBlockingQueue)
- 原子变量(Atomic*)
同步代码块(synchronized)
synchronized关键字可以用于方法中的某个区块中,表示只对该区块的资源进行互斥访问。
synchronized(同步锁){
需要同步的代码
}
同步锁
多个线程使用同一把锁,在一个线程拥有同步锁的时候,该线程就可以进入代码块,其他线程只能在外面等着。
同步方法(synchronized)
public synchronized void methon(){
需要同步的代码
}
同步锁(ReenreantLock)
public void lock(); // 开启同步锁
public void unlock(); // 释放同步锁
try{
lock();
....
}finally{
unlock();
}
死锁
死锁是多个线程互相抢占资源的造成的一种僵局,导致线程之间相互等待,若没有外力作用,线程将永远等待下去。
死锁产生的必要条件
若产生死锁,则下面条件必然成立。
- 互斥条件,即进程对所分配的进程
- 不可剥夺条件
- 占有并请求条件
- 循环等待条件
#### 死锁预防
防止死锁产生的四个必要条件成立即可。
死锁避免
- 有序资源分配法
- 银行家算法
- 顺序加锁
- 限时加锁
死锁检测
预防和避免死锁系统开销大且不能充分利用资源,更好的方法是不采取任何限制性措施,而是提供检测和解脱死锁的手段,这就是死锁检测和恢复。
死锁检测数据结构:
- E是现有资源向量(existing resource vector),代码每种已存在资源的总数
- A是可用资源向量(available resource vector),那么Ai表示当前可供使用的资源数(即没有被分配的资源)
- C是当前分配矩阵(current allocation matrix),C的第i行代表Pi当前所持有的每一种类型资源的资源数
- R是请求矩阵(request matrix),R的每一行代表P所需要的资源的数量

死锁检测步骤
寻找一个没有结束标记的进程Pi,对于它而言R矩阵的第i行向量小于或等于A。
如果找到了这样一个进程,执行该进程,然后将C矩阵的第i行向量加到A中,标记该进程,并转到第1步
如果没有这样的进程,那么算法终止
算法结束时,所有没有标记过的进程都是死锁进程。
死锁恢复
- 利用抢占资源恢复,即临时将某个资源从它的当前所属进程转移到另一个进程,相当于破坏了死锁产生的必要条件的不可剥夺条件。
- 利用回滚恢复,周期性的将进程的状态进行备份,当发现进程死锁后,根据备份将进程复位到一个更早的,还没有取得所需资源的状态,接着就把这些资源分配给其他进程。
- 通过杀死进程恢复,直接将一个或多个进程杀死,相当于破坏了循环等待条件。
多线程特性
当线程参与计算时,原始的数据来自内存,在计算过程中,有些数据可能被频繁读取,这些数据被临时存储在寄存器和高速缓存中,当线程完成计算任务后,这些缓存的数据会写回内存。
当多个线程同时读写某个内存数据时,就会产生多线程并发问题,解决这些问题涉及到多线程编程的三个特性:原子性,有序性,可见性。
多线程编程应该满足三个特性:原子性,可见性,有序性。
原子性
一个或多个操作要么全部执行成功要么就都不执行。
可见性
当多个线程访问同一变量时,一个线程修改了这个变量的值,其他线程应该能够立即看到修改的值。
有序性
程序的执行顺序按照代码的先后顺序执行。
多线程控制类
为了保证多线程的三个特性,Java引入了很多线程控制机制:
- ThreadLocal
- 原子类
- Lock类
- Volatile关键字
ThreadLocal
未完待续~
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 572108581@qq.com
文章标题:多线程
文章字数:1.7k
本文作者:ZSH
发布时间:2019-11-17, 15:51:27
最后更新:2019-12-19, 19:07:30
原始链接:https://zhongshanhao.github.io/2019/11/17/multiThread/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。