1.线程上下文切换
缘由:cpu个数小于线程个数,为了让用户觉得所有程序都在并行进行,需要cpu切换时间片。线程A时间片结束后,为了下一次再次运行线程A时知道该从哪里继续运行,需要保存线程A的上下文信息。
线程切换时机:线程的cpu时间片使用完毕时,线程被其他线程中断时。
2.线程死锁
从表现定义死锁:两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象。在无外力干涉情况下,这种情况会持续等待下去。
2.1 死锁产生的四个条件
互斥条件:某资源同一时刻只能被一个线程占用。
请求并持有条件:线程持有至少一个资源,但又请求新的正在被其他线程使用的资源。
不可剥夺条件:线程再使用完毕资源之前不可被其他线程剥夺。
环路等待条件:存在线程、资源的环形链。线程集合{T0,T1,...,Tn},线程T0等待T1资源...线程Tn等待T0资源。
线程死锁代码实例:
threadA开启后占用resourceA,睡眠5s,threadB开启后占用resourceB,1s后睡眠结束,获取resourceA失败阻塞,知道threadA释放resourceA才继续执行下去。
private static Object resourceA = new Object();
private static Object resourceB = new Object();
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA) {
try {
System.out.println("线程A获得resourceA,开始等待");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (resourceB) {
System.out.println("线程A获得resourceB");
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceB) {
try {
System.out.println("线程B获得resourceB,开始等待");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (resourceA) {
System.out.println("线程B获得resourceA");
}
}
});
threadA.start();
threadB.start();
}
输出:
线程B获得resourceB,开始等待
线程A获得resourceA,开始等待
线程A获得resourceB
线程B获得resourceA
2.2 如何避免线程死锁
只需破坏线程死锁4个条件之一即可。但在实际开发中,只有请求保持、环路等待两个条件可被破坏。不可剥夺和互斥条件,是线程安全必备的。
具体方法:有序申请资源。
假如线程A和B都需要资源1,2,3,...,n,对资源排序,线程A和B只有在获取了资源n-1时才能获取资源n。也就是线程A和B同时、有序获取资源1,2,3,...,n