线程状态
一,概念介绍
**当我们在编写多线程程序时,了解线程状态是非常重要的,因为它能够帮助我们理解线程在执行过程中的行为,从而更好地控制和管理线程。**
下面是对线程状态概念的详细介绍:
-
新建状态(New):在这个状态下,线程对象已经被创建,但是还没有调用
start()
方法启动线程。此时,线程对象只是一个普通的Java对象,还没有被分配操作系统资源。 -
就绪状态(Runnable):当调用了线程对象的
start()
方法后,线程就进入了就绪状态。在这个状态下,线程已经被加入到线程调度器的就绪队列中,等待被分配CPU时间片来执行任务。 -
运行状态(Running):当线程获得了CPU时间片,开始执行任务时,它处于运行状态。在这个状态下,线程正在执行自己的任务代码。
-
阻塞状态(Blocked):线程进入阻塞状态通常是因为某些原因导致了线程无法继续执行。常见的阻塞原因包括等待I/O操作完成、等待获取锁、等待条件满足等。当阻塞的原因消失后,线程会重新进入就绪状态等待执行。
-
等待状态(Waiting):线程进入等待状态是因为它在某个对象上等待。例如,线程调用了
Object.wait()
方法或者Thread.join()
方法时会进入等待状态。在等待状态下,线程会释放掉它所持有的锁,直到其他线程唤醒它。 -
定时等待状态(Timed Waiting):和等待状态类似,但是在这个状态下,线程在等待一段时间或者等待某个条件满足之前会超时返回。例如,调用
Thread.sleep()
方法或者Object.wait(timeout)
方法时会使线程进入定时等待状态。 -
终止状态(Terminated):线程处于终止状态表示它已经执行完任务或者被提前中断。当线程的
run()
方法执行完毕或者调用了Thread.interrupt()
方法中断线程时,线程会进入终止状态。
理解线程状态的转换和含义可以帮助开发人员编写出更加可靠和高效的多线程程序,避免出现死锁、竞态条件等并发问题。
二,代码实现介绍
public static void main(String[] args) {
// 创建两个新线程
Thread thread1 = new Thread(new MyRunnable(), "线程1");
Thread thread2 = new Thread(new MyRunnable(), "线程2");
// 打印初始状态
printThreadState(thread1, "初始化");
printThreadState(thread2, "初始化");
// 启动线程1
thread1.start();
// 等待一段时间,确保线程1进入就绪态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
printThreadState(thread1, "启动");
// 启动线程2
thread2.start();
// 等待一段时间,确保线程2进入就绪态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
printThreadState(thread2, "启动");
// 中断线程1,使其进入阻塞态
thread1.interrupt();
// 等待一段时间,确保线程1进入阻塞态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
printThreadState(thread1, "中断");
// 中断线程2,使其进入等待态
thread2.interrupt();
// 等待一段时间,确保线程2进入等待态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
printThreadState(thread2, "中断");
}
static void printThreadState(Thread thread, String action) {
System.out.println("线程[" + thread.getName() + "]执行" + action + ",状态:" + thread.getState());
}
static class MyRunnable implements Runnable {
@Override
public void run() {
try {
// 让线程进入运行态
Thread.sleep(200);
System.out.println("线程正在执行任务");
} catch (InterruptedException e) {
// 捕获中断异常
System.out.println("线程被中断");
}
}
}
我添加了一些等待时间以确保我们可以观察到线程的就绪态。现在,我们可以在启动线程之后立即观察到它们的就绪态。希望这样更清晰明了。
三,死锁
线程死锁通常由四个必要条件造成,这些条件是:
-
互斥条件(Mutual Exclusion):至少有一个资源必须处于非共享模式,即一次只能被一个进程使用。如果一个进程在使用该资源时,其他进程无法访问或使用它,就称这种情况为互斥条件。
-
持有并等待(Hold and Wait):一个进程可以请求并持有多个资源,并且在等待其他资源时不释放当前已经持有的资源。这样就会导致其他进程无法访问被当前进程持有的资源,从而可能造成死锁。
-
不可抢占条件(No Preemption):已经分配给一个进程的资源不能被强制性地抢占,只能在进程使用完之后由进程自己释放。这意味着其他进程无法强行剥夺另一个进程所持有的资源,只能通过协商或等待来获取资源。
-
循环等待(Circular Wait):存在一个进程等待队列 {P1, P2, …, Pn},其中P1等待P2持有的资源,P2等待P3持有的资源,…,Pn等待P1持有的资源,形成一个环形等待的情况。
当这四个条件同时满足时,就可能导致线程死锁的发生。因此,为了避免死锁,需要在设计和实现并发系统时尽量避免这些条件的出现。
演示了如何创建一个可能导致线程死锁的情况
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
// 线程1尝试获取resource1,然后获取resource2
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
// 线程2尝试获取resource2,然后获取resource1
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 2...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 1...");
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 2 and resource 1...");
}
}
});
// 启动两个线程
thread1.start();
thread2.start();
// 等待两个线程结束
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
在这个示例中,有两个线程分别尝试获取resource1和resource2,但它们的获取顺序相反,即线程1先获取resource1再获取resource2,而线程2先获取resource2再获取resource1。这种情况下,如果线程1获取了resource1,而线程2获取了resource2,然后它们都试图等待对方释放另一个资源,就可能发生死锁。
1.互斥条件(Mutual Exclusion):在代码中,资源resource1和resource2都是以synchronized关键字锁定的,这意味着同一时刻只能有一个线程访问它们,从而满足了互斥条件。
2.持有并等待(Hold and Wait) :每个线程在持有一个资源的同时,又试图获取另一个资源,例如,线程1在持有resource1时等待获取resource2,而线程2在持有resource2时等待获取resource1。因此,这种情况满足了持有并等待条件。
另外两个条件在这个简单的示例中并没有被完全满足:
3.不可抢占条件(No Preemption):示例中并没有体现这个条件,因为一旦一个线程获得了资源,其他线程不能强制性地抢占该资源,只能等待资源被释放。
4.循环等待(Circular Wait) :示例中存在循环等待,即线程1等待线程2持有的资源,而线程2又等待线程1持有的资源,形成了一个循环等待的情况。
破坏死锁条件示例代码
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
// 线程1尝试获取resource1,然后获取resource2
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
// 线程2尝试获取resource1,然后获取resource2
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
// 启动两个线程
thread1.start();
thread2.start();
// 等待两个线程结束
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
在这个修改后的版本中,线程1和线程2都按照相同的顺序获取资源,因此不会出现循环等待的情况,从而可以避免死锁。
以下是对不可抢占条件和持有并等待条件的示例代码:
1.不可抢占条件(No Preemption)
public class NoPreemptionExample {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 2...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 1...");
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
在这个示例中,线程1持有resource1
并等待获取resource2
,而线程2持有resource2
并等待获取resource1
,由于资源无法被强制性地抢占,因此可能会发生死锁。
不可抢占条件是指已经被一个线程占用的资源不能被强制性地抢占。解决不可抢占条件的常见方法包括资源分配顺序、超时等待和资源预先分配等。以下是这些方法的示例:
不可抢占条件死锁,资源分配顺序解决方案
public class NoPreemptionSolution {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) { // 交换资源1和资源2的获取顺序
System.out.println("Thread 2: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
通过调整线程获取资源的顺序,可以避免死锁。在这个示例中,线程2先尝试获取resource1
,然后再尝试获取resource2
,与线程1获取资源的顺序相反,从而避免了死锁。
不可抢占条件死锁,超时等待解决方案
public class NoPreemptionSolution {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < 1000) { // 设置超时时间
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
break;
}
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 2...");
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < 1000) { // 设置超时时间
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
break;
}
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
通过在尝试获取资源时设置超时时间,如果超过了设定的时间仍未获取到资源,线程可以放弃等待并执行其他操作,从而避免死锁。
不可抢占条件死锁 资源预先分配解决方案
public class NoPreemptionSolution {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Holding resource 1 and resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Holding resource 1 and resource 2...");
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
在某些情况下,可以通过在程序开始时就分配好所有需要的资源,从而避免死锁的发生。在这个示例中,线程1和线程2都先获取了所有需要的资源,从而避免了死锁的发生。
2,持有并等待条件(Hold and Wait)
public class HoldAndWaitExample {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
thread1.start();
// 在启动线程2之前,让线程1先持有resource1
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
在这个示例中,线程1在持有resource1
的同时等待获取resource2
,而线程2在启动之前先让线程1持有了resource1
,这种情况下可能会发生死锁。
持有并等待条件是指一个线程在持有至少一个资源的同时,又等待获取其他线程持有的资源,从而导致了资源竞争和可能的死锁情况。解决这种条件的方法通常包括资源分配顺序、资源预分配和使用信号量等。
以下是针对持有并等待条件的一些解决方案:
资源分配顺序
通过规定线程获取资源的顺序,可以避免持有并等待条件。确保线程在获取资源之前不会持有其他资源,从而降低了发生资源竞争和死锁的可能性。
public class HoldAndWaitSolution {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
在这个示例中,线程1先获取resource1
,然后尝试获取resource2
,而线程2在获取resource1
之前等待。
资源预分配
在程序开始时就分配好所有需要的资源,从而避免线程在运行过程中等待其他资源。这种方法可以减少资源竞争和死锁的可能性。
public class HoldAndWaitSolution {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
synchronized (resource1) {
System.out.println("Main thread: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("Main thread: Holding resource 1 and resource 2...");
}
}
System.out.println("Main thread exiting...");
}
}
在这个示例中,主线程在开始时就先获取了所有需要的资源,从而避免了持有并等待条件的发生。
这些解决方案可以帮助避免持有并等待条件,从而降低了死锁的风险。
以下是对循环等待条件和互斥条件的示例代码:
3.循环等待条件(Circular Wait)
public class CircularWaitExample {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 2...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 1...");
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
在这个示例中,线程1持有resource1
并等待获取resource2
,而线程2持有resource2
并等待获取resource1
,形成了循环等待的情况。
循环等待条件是指一组进程(线程)之间形成了一个循环链,每个进程都在等待下一个进程所持有的资源。解决循环等待条件的方法通常包括资源排序、资源分级和使用银行家算法等。
以下是针对循环等待条件的一些解决方案:
资源排序
通过为所有资源分配一个全局唯一的序号,并规定进程必须按照序号递增的顺序获取资源,从而避免形成循环等待条件。
public class CircularWaitSolution {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1...");
try {
Thread.sleep(100); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
在这个示例中,我们规定线程必须先获取resource1
,然后再尝试获取resource2
,从而避免了循环等待条件的发生。
资源分级
通过为资源定义不同的优先级,进程在获取资源时必须按照一定的顺序进行,从而避免形成循环等待条件。
银行家算法
银行家算法是一种用于避免死锁的动态资源分配算法。它是由Edsger Dijkstra于1968年提出的,并以银行家和客户之间的关系来比喻资源管理和请求资源的过程。
基本思想:
-
安全状态检查(Safety Check):在分配资源之前,系统会检查分配资源后是否会进入不安全状态。如果进入不安全状态,系统会拒绝该资源请求,以避免死锁。
-
资源分配(Resource Allocation):当进程请求资源时,系统会检查是否有足够的资源可用。如果有,则分配资源给进程;否则,进程必须等待,直到有足够的资源为止。
-
资源释放(Resource Release):进程完成任务后释放已占用的资源,以便其他进程可以使用。
算法描述:
-
初始化:对于每种资源类型,记录当前可用的资源数目和每个进程所需的资源数目。
-
资源请求:当进程请求资源时,系统会检查是否有足够的资源可用。如果有,则分配资源给进程;否则,进程必须等待,直到有足够的资源为止。
-
安全性检查:系统在分配资源之前会检查是否会进入不安全状态。安全状态指的是,系统可以为每个进程找到一个安全序列,使得每个进程都能够顺利完成并释放已占用的资源。
-
资源释放:进程完成任务后释放已占用的资源,以便其他进程可以使用。
算法实现:
下面是一个简单的Java实现银行家算法的示例:
import java.util.Arrays;
public class BankerAlgorithm {
private int[][] max; // 最大需求矩阵
private int[][] allocated; // 已分配资源矩阵
private int[] available; // 可用资源向量
private int[][] need; // 需求资源矩阵
private int[] safeSequence; // 安全序列
public BankerAlgorithm(int[][] max, int[][] allocated, int[] available) {
this.max = max;
this.allocated = allocated;
this.available = available;
this.need = new int[max.length][max[0].length];
this.safeSequence = new int[max.length];
calculateNeed();
}
// 计算需求资源矩阵
private void calculateNeed() {
for (int i = 0; i < max.length; i++) {
for (int j = 0; j < max[i].length; j++) {
need[i][j] = max[i][j] - allocated[i][j];
}
}
}
// 检查进程是否能安全执行
public boolean isSafe() {
int processes = max.length;
boolean[] finish = new boolean[processes];
int[] work = Arrays.copyOf(available, available.length);
int count = 0;
while (count < processes) {
boolean found = false;
for (int i = 0; i < processes; i++) {
if (!finish[i] && isProcessSafe(i, work)) {
for (int j = 0; j < work.length; j++) {
work[j] += allocated[i][j];
}
safeSequence[count++] = i;
finish[i] = true;
found = true;
}
}
if (!found) {
return false; // 不存在安全序列
}
}
return true; // 存在安全序列
}
// 检查某个进程是否能安全执行
private boolean isProcessSafe(int process, int[] work) {
for (int i = 0; i < available.length; i++) {
if (need[process][i] > work[i]) {
return false;
}
}
return true;
}
// 获取安全序列
public int[] getSafeSequence() {
return safeSequence;
}
}
使用示例:
public class Main {
public static void main(String[] args) {
int[][] max = {{7, 5, 3}, {3, 2, 2}, {9, 0, 2}, {2, 2, 2}, {4, 3, 3}};
int[][] allocated = {{0, 1, 0}, {2, 0, 0}, {3, 0, 2}, {2, 1, 1}, {0, 0, 2}};
int[] available = {3, 3, 2};
BankerAlgorithm banker = new BankerAlgorithm(max, allocated, available);
if (banker.isSafe()) {
int[] safeSequence = banker.getSafeSequence();
System.out.println("Safe sequence: " + Arrays.toString(safeSequence));
} else {
System.out.println("No safe sequence exists.");
}
}
}
这个示例演示了一个包含5个进程和3种资源的系统,通过银行家算法检查是否存在安全序列。如果存在安全序列,则表示系统可以顺利执行所有进程而不会进入死锁状态。
通过这些方法,可以有效地解决循环等待条件,从而降低了系统发生死锁的风险。
4.互斥条件(Mutual Exclusion)
互斥条件是指资源每次只能被一个线程占用。以下示例展示了互斥条件:
public class MutualExclusionExample {
private static Object resource = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource) {
System.out.println("Thread 1: Holding the resource...");
try {
Thread.sleep(1000); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Done with the resource.");
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource) {
System.out.println("Thread 2: Holding the resource...");
try {
Thread.sleep(1000); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Done with the resource.");
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
在这个示例中,线程1和线程2分别持有resource
,并在持有期间执行一些操作,由于互斥条件,资源在同一时刻只能被一个线程占用,因此这两个线程无法同时访问资源,不会发生死锁。
如果 thread1
先获取到了 resource
锁,并在同步块内部执行一些操作时,如果此时 thread2
也尝试获取 resource
锁,但由于该锁已经被 thread1
持有,因此 thread2
会被阻塞,直到 thread1
释放锁。但是,如果 thread1
在执行过程中需要等待 thread2
持有的其他资源,则 thread1
会一直等待,而 thread2
也无法释放 resource
锁,从而导致死锁。
为了避免死锁,可以按照一定的顺序获取锁,或者限制同一时间只能获取一个锁。以下是修改后的示例代码:
public class MutualExclusionExample {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(1000); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 2...");
System.out.println("Thread 1: Done with resources.");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 2...");
try {
Thread.sleep(1000); // 模拟执行一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1...");
System.out.println("Thread 2: Done with resources.");
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread exiting...");
}
}
在修改后的代码中,我们使用了两个资源 resource1
和 resource2
,并确保线程在获取资源时按照相同的顺序进行获取,从而避免了死锁的发生。