线程唤醒机制
线程唤醒机制是用来解决线程之间通信问题的一种机制,我们知道多个线程之间是通过竞争来活得CPU资源的,但是这样线程之间是竞争关系的,但是当我们有些时候需要线程之间一起协作来完成任务的,这个时候救出想了线程唤醒机制。多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。
等待池
等待池就是一个池子,这个池子里面放着我们所有等待被唤醒的线程
首先我们看看使用的几个方法
wait() 让线程进入等待状态,并且释放锁
wait(long timeout) 让线程进入等待状态释放锁,并在达到指定时间时自动唤醒
notify() 唤醒等待池中的一个线程
notifyAll() 唤醒等待池中的所有线程
代码胜于雄辩
package experiment;
import java.io.*;
public class C {
public static void main(String[] args) throws IOException, InterruptedException {
Object lock1=new Object();//锁一,锁即使线程争夺的资源
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock1){
System.out.println("线程1进来了...");
try {
lock1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1出来了");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock1){
System.out.println("线程2进来了...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2出来了");
lock1.notify();
}
}
}).start();
}
}
运行结果:
可以看到当我们进去线程1之后,并没有直接输出线程1出来了,而是让我们的线程1进入等待,知道线程2执行完成唤醒之后才开始执行后面的代码
关于线程唤醒机制有几个需要注意的点:
wait()、notify()、notify()必须在同步代码块中使用
wait()会释放锁,而sleep()不会,锁一旦被释放就会重新被竞争
死锁
什么是死锁?
死锁是一种由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
产生死锁的原因,主要包括:
- 系统资源不足;
- 程序执行的顺序有问题;
- 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,那么死锁出现的可能性就很低;否则,
就会因争夺有限的资源而陷入死锁。其次,程序执行的顺序与速度不同,也可能产生死锁。产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
package experiment;
import java.io.*;
public class C {
public static void main(String[] args) throws IOException, InterruptedException {
Object lock1=new Object();//锁一,锁即使线程争夺的资源
Object lock2=new Object();//锁二,锁即使线程争夺的资源
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock1){
System.out.println("线程1拿到了锁一资源");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock2){
System.out.println("线程1拿到了锁二资源");
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock2){
System.out.println("线程2拿到了锁一资源");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock1){
System.out.println("线程2拿到了锁二资源");
}
}
}
}).start();
}
}
这里我们来分析一下这段代码,这里两个线程都去获取锁一和锁二资源
线程1先获取锁一再获取锁二
线程2先获取锁二再获取锁一
线程1首先获取到了锁一资源,但是当要去获取锁二的时候,这个时候锁二被线程2获取到了,锁一想要获取锁二必须等待线程2释放锁二它才能获取到,当线程二想要去获取锁一资源的时候,锁一还在被线程1占据者,线程1等待线程2 的资源,线程2又等待线程1的资源,这个时候就形成了”循环等待状态“,也就造成了死锁
如何解决死锁?
- 一次性分配所有资源,这样就不会再有请求了
- 即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源
- 系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反,即当一个线程拿到一个资源之后,只能申请更高等级的资源
如有错误,欢迎联系改正!!!感谢阅读