一、死锁
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。
什么情况下会产生死锁
- 资源有限
- 同步嵌套
public class Csdn {
public static void main(String[] args) {
Object objA = new Object();
Object objB = new Object();
new Thread(() -> {
while (true) {
synchronized (objA) {
synchronized (objB) {
System.out.println("小康同学正在走路");
}
}
}
}).start();
new Thread(() -> {
while (true) {
synchronized (objB) {
synchronized (objA) {
System.out.println("小薇同学正在走路");
}
}
}
}).start();
}
}
死锁的解决:
- 不使用锁的嵌套,从根本上解决死锁问题。
- 当某个线程长期无法获取想要的锁的时候,让它主动释放掉手动持有的锁,让其他线程先执行,再尝试获取锁。
public class Csdn {
public static void main(String[] args) {
Object objA = new Object();
Object objB = new Object();
Object objC = new Object();
new Thread(() -> {
while (true) {
synchronized (objC) {
synchronized (objB) {
System.out.println("小康同学正在走路");
}
}
}
}).start();
new Thread(() -> {
while (true) {
synchronized (objB) {
synchronized (objA) {
System.out.println("小薇同学正在走路");
}
}
}
}).start();
}
}
二、生产者消费者问题
1.生产者消费者模式概述
Object类的等待和唤醒方法
方法名 | 说明 |
---|---|
void wait() | 当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法 |
void notify() | 唤醒正在等待对象监视器的单一线程 |
void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。
所谓生产者消费者问题,实际上主要是包含了两类线程:
- 生产者线程,用于生产数据
- 消费者线程,用于消费数据
为了解耦生产者和消费者的关系,通常会采用共享的数据区域。
- 生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;
- 消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为。
2.生产者消费者案例
需求
- 桌子类(Desk):定义表示包子数量的变量,定义锁对象变量,定义标记桌子上有无包子的变量
- 生产者(Cokker):继承Thread类,重写run()方法,设置线程任务
- 判断是否有包子,决定当前线程是否执行
- 如果有包子,就进入等待状态;如果没有包子,继续执行生产包子
- 生产包子之后,更新桌子上包子状态,唤醒消费者消费包子
- 消费者类(Foodie):继承Thread类,重写run()方法,设置线程任务
- 判断是否有包子,决定当前线程是否执行
- 如果没有包子,就进入等待状态;如果有包子,就消费包子
- 消费包子后,更新桌子上包子状态,唤醒生产者生产包子
- 测试类(Csdn):里面有main方法,main方法总的代码步骤如下:
- 创建生产者线程和消费者线程对象
- 分别开启两个线程
class Desk {
private boolean flag;
private int count;
private final Object lock = new Object();
public Desk() {
this(false, 10);
}
public Desk(boolean flag, int count) {
this.flag = flag;
this.count = count;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Object getLock() {
return lock;
}
@Override
public String toString() {
return "Desk{" +
"flag=" + flag +
", count=" + count +
", lock=" + lock +
'}';
}
}
class Cooker extends Thread {
private Desk desk;
public Cooker(Desk desk) {
this.desk = desk;
}
@Override
public void run() {
while (true) {
synchronized (desk.getLock()) {
if (desk.getCount() == 0) {
break;
} else {
if (!desk.isFlag()) {
System.out.println("厨师正在生产包子");
desk.setFlag(true);
desk.getLock().notifyAll();
} else {
try {
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
class Foodie extends Thread {
private Desk desk;
public Foodie(Desk desk) {
this.desk = desk;
}
@Override
public void run() {
while (true) {
synchronized (desk.getLock()) {
if (desk.getCount() == 0) {
break;
} else {
if (desk.isFlag()) {
System.out.println("吃货在吃包子");
desk.setFlag(false);
desk.getLock().notifyAll();
desk.setCount(desk.getCount() - 1);
} else {
try {
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Csdn {
public static void main(String[] args) {
Desk desk = new Desk();
Foodie f = new Foodie(desk);
Cooker c = new Cooker(desk);
f.start();
c.start();
}
}
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
厨师正在生产包子
吃货在吃包子
三、阻塞队列
1.阻塞队列概述
常见BlockingQueue:
- ArrayBlockingQueue:底层是数组,有界
- LinkedBlockingQueue:底层是链表,无界(不是真正的无界,最大为int的最大值)
BlockingQueue的核心方法:
方法名 | 说明 |
---|---|
void put(E e) | 将参数放入队列,如果放不进去会阻塞 |
E take() | 取出第一个数据,取不到会阻塞 |
2.阻塞队列实现生产者消费者问题
需求
- 生产者类(Cooker):继承Thread类,重写run()方法,设置线程任务
- 构造方法中接收一个阻塞队列对象
- 在run方法中循环向阻塞队列中添加包子
- 打印添加结果
- 消费者类(Foodie):继承Thread类,重写run()方法,设置线程任务
- 构造方法中接收一个阻塞队列对象
- 在run方法中循环获取阻塞队列中的包子
- 打印获取结果
- 测试类(Csdn):里面有main方法,main方法中的代码步骤如下:
- 创建阻塞队列对象
- 创建生产者线程和消费者线程对象,构造方法中传入阻塞队列对象
- 分别开启两个线程
class Cooker extends Thread {
private ArrayBlockingQueue<String> bd;
public Cooker(ArrayBlockingQueue<String> bd) {
this.bd = bd;
}
@Override
public void run() {
while (true) {
try {
bd.put("包子");
System.out.println("厨师正在生产包子");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Foodie extends Thread {
private ArrayBlockingQueue<String> bd;
public Foodie(ArrayBlockingQueue<String> bd) {
this.bd = bd;
}
@Override
public void run() {
while (true) {
try {
String take = bd.take();
System.out.println("吃货在吃" + take);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Csdn {
public static void main(String[] args) {
ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1);
Foodie f = new Foodie(bd);
Cooker c = new Cooker(bd);
f.start();
c.start();
}
}