小猫🐱线程
前言
早晨看到一个视频有感,视频中两只小猫互相轮流吃猫粮,由此我想到了并发编程中的"资源竞争"和“线程交替执行”现象(emmm学疯了,满脑子都是计算机专业术语)
图中的猫粮可以看作是 CPU 资源,两只小猫同时吃猫粮,可以看作是是两个线程同时在争夺 CPU 资源。因为两只猫都很有礼貌(孔🐱让猫粮hhh),会轮流吃,相当于并发编程中的 “多线程的交替执行” ;但是还有另一种情况,如果一只猫(一个线程)更强势,它可能吃掉大部分猫粮(占据大部分的资源),而另一只猫只能“饿着”,这对应于多线程中的 “资源饥饿”、“资源竞争” 问题。

下面让我们重温下线程交替执行的几种实现方式,以及如何应对资源竞争问题。
线程交替执行
要求A B 线程交替执行,最终输出如下图所示
需求,有两个线程,A线程内容是输出数字1到26,B线程内容是输出字母a-z
在 Java 中,实现线程交替执行的方式有多种,例如,标志位+synchronized+wait/notify方法、标志位+condition机制
标志位+synchronized+wait/notify
实现思路:通过 synchronized
关键字和 Object
类的 wait()
和 notify()
方法,多个线程可以协调并交替执行。wait()
方法会让线程进入等待状态,直到接收到其他线程通过 notify()
或 notifyAll()
发送的通知。
public class AlternatingExecution1 {
// 锁
private static final Object lock = new Object();
// 标志位,isFirst为true表示PrintTask1线程执行,为false表示PrintTask2线程执行
private static boolean isFirst = true;
static class PrintTask1 implements Runnable {
@Override
public void run() {
synchronized (lock) {
for (int i = 1; i <= 26; i++) {
try {
while (!isFirst) {
// 等待,释放资源
lock.wait();
}
// 工作
System.out.print(i + " ");
isFirst = false; // 切换
lock.notify(); // 唤醒另一个线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
static class PrintTask2 implements Runnable {
@Override
public void run() {
synchronized (lock) {
for (char i = 'a'; i <= 'z'; i++) {
try {
while (isFirst) {
// 等待释放资源
lock.wait();
}
System.out.print(i + " ");
isFirst = true; // 切换
lock.notify(); // 唤醒另一个线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new PrintTask1());
Thread t2 = new Thread(new PrintTask2());
t1.start();
t2.start();
}
}
标志位+lock_condition机制
实现思路:和wait_notiy实现的基本一致
改变地方:wait变成了await,notify变成了signal,lock使用了ReentrantLock(
public class AlternatingExecution2 {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
private static boolean isFirst = true;
static class PrintTask1 implements Runnable {
@Override
public void run() {
lock.lock();
for (int i = 1; i <= 26; i++) {
try {
while (!isFirst) {
// 等待并释放资源,相当于 lock.wait()
condition.await();
}
// 工作
System.out.print(i + " ");
isFirst = false; // 切换
// 唤醒其他线程,相当于 lock.notifyAll()
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
}
static class PrintTask2 implements Runnable {
@Override
public void run() {
lock.lock();
for (char i = 'a'; i <= 'z'; i++) {
try {
while (isFirst) {
// 释放资源
condition.await();
}
System.out.print(i + " ");
isFirst = true; // 切换
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new PrintTask1());
Thread t2 = new Thread(new PrintTask2());
t1.start();
t2.start();
}
}
扩展:三线程交替执行
要求A B C线程交替执行,A线程输出数字、B线程输出小写字母、C线程输出大写字母。最终输出如下图所示:
实现思路:将标志位改为String类型的,A线程执行完毕之后将标志位改为B、B线程执行完毕之后将标志位改为C、C线程执行完毕之后将标志位改为A
public class AlternatingExecution3 {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
// 标志位
private static String falg = "A";
static class PrintTask1 implements Runnable {
@Override
public void run() {
lock.lock();
for (int i = 1; i <= 26; i++) {
try {
while (!falg.equals("A")) {
// 等待并释放资源,相当于 lock.wait()
condition.await();
}
// 工作
System.out.print(i + " ");
falg = "B"; // 切换
// 唤醒其他线程,相当于 lock.notifyAll()
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
}
static class PrintTask2 implements Runnable {
@Override
public void run() {
lock.lock();
for (char i = 'a'; i <= 'z'; i++) {
try {
while (!falg.equals("B")) {
// 释放资源
condition.await();
}
System.out.print(i + " ");
falg = "C"; // 切换
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
}
static class PrintTask3 implements Runnable {
@Override
public void run() {
lock.lock();
for (char i = 'A'; i <= 'Z'; i++) {
try {
while (!falg.equals("C")) {
// 释放资源
condition.await();
}
System.out.print(i + " ");
falg = "A"; // 切换
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new PrintTask1());
Thread t2 = new Thread(new PrintTask2());
Thread t3 = new Thread(new PrintTask3());
t1.start();
t2.start();
t3.start();
}
}
资源竞争
如何解决猫的“竞争”问题?
- 锁机制:给猫条设置一个“锁”(例如,人控制着猫粮),两只猫只能轮流吃
- 优先级调度:给其中一只猫更高的优先级,确保它先吃(例如,年纪小的猫优先)
- 资源分片:把猫粮分成两段,各自一半,避免直接竞争
- 公平竞争:通过计时器,控制两只猫按固定的时间间隔轮流吃