目录
前言
本文模拟了一个多线程发送礼物的场景,旨在演示线程调度和锁机制的工作原理。在该场景中,两个线程分别代表小红和小明负责从礼物列表中随机取出礼物进行发送。我们将通过代码实例来演示如何通过适当的线程休眠和同步机制来避免线程独占锁的现象,并确保线程安全。在调试过程中,我们发现了线程安全问题,并通过优化方式使两个线程能够均匀分配发送任务。
1. 问题描述
在最初的测试中,发现存在以下问题:
-
小红线程总是先获取到锁,并连续发送多个礼物,而小明线程往往无法参与礼物的发送。
-
在极端情况下,小红甚至会将所有礼物发送完,而小明线程却没有机会发送任何礼物。
原因分析: 这是因为在多线程调度中,CPU 分配执行权的速度非常快,导致一个线程有可能在短时间内连续获得锁资源,从而造成另一个线程“饥饿”。为了解决这个问题,我们需要引入 Thread.sleep()
进行适当的线程休眠,让两个线程都有机会竞争锁资源。
2. 代码展示
以下是该礼物发送系统的主要代码实现:
主类:Demo1
// Java 多线程发送礼物代码示例
package com.takumilove.d7_test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
// 初始化礼物列表
List<String> gift = new ArrayList<>();
String[] names = {"包包", "鞋子", "衣服", "裤子", "帽子", "围巾", "手套", "袜子", "内衣", "外套"};
Random r = new Random();
// 随机生成 100 个礼物
for (int i = 0; i < 100; i++) {
gift.add(names[r.nextInt(10)] + (i + 1));
}
System.out.println("初始化礼物列表: " + gift);
// 启动两个线程:小红和小明
SendThread xh = new SendThread(gift, "小红");
SendThread xm = new SendThread(gift, "小明");
xh.start();
xm.start();
// 等待两个线程结束
xh.join();
xm.join();
// 输出每个线程发送的礼物数量
System.out.println("小红发出了:" + xh.getCount() + " 个礼物");
System.out.println("小明发出了:" + xm.getCount() + " 个礼物");
}
}
礼物发送线程:SendThread
package com.takumilove.d7_test;
import java.util.List;
import java.util.Random;
public class SendThread extends Thread {
private List<String> gift;
private int count;
public SendThread(List<String> gift, String name) {
super(name);
this.gift = gift;
}
public int getCount() {
return count;
}
@Override
public void run() {
Random r = new Random();
while (true) {
synchronized (gift) {
// 当礼物数量少于 10 时停止发送
if (gift.size() < 10) {
break;
}
// 随机发送礼物并记录发送数量
System.out.println(Thread.currentThread().getName() + " 发出了:" + gift.remove(r.nextInt(gift.size())));
count++;
// 加入线程休眠,防止一个线程独占锁
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
3. 问题分析
在初始版本中,由于 synchronized
锁机制的特点,当一个线程获取到 gift
锁时,它会占用该资源,直到退出临界区为止。在礼物数量大于 10 时,线程往往会在短时间内连续运行,导致只有一个线程能抢到所有的锁资源,形成“线程独占”现象。
4. 解决方案
通过在每次发送完礼物后加入一个 Thread.sleep(1)
语句,让线程主动释放 CPU 执行权,避免线程独占的情况,从而让其他线程有机会竞争锁资源。虽然线程休眠时间只有 1 毫秒,但在实际调度中能够显著改善线程调度的不均匀问题。
修改后的代码:
@Override
public void run() {
Random r = new Random();
while (true) {
synchronized (gift) {
if (gift.size() < 10) {
break;
}
System.out.println(Thread.currentThread().getName() + " 发出了:" + gift.remove(r.nextInt(gift.size())));
count++;
try {
// 增加线程休眠时间,防止一个线程独占锁
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5. 效果
经过该调整后,小红和小明在发送礼物的过程中变得更加均匀,不再出现一个线程独占锁的现象。通过对每个线程的礼物发送数量统计可以看到,两者的礼物数量分配更加接近,线程调度更合理。
6. 总结
通过本文的多线程礼物发送场景模拟,我们展示了如何在并发编程中,通过引入适当的 Thread.sleep()
机制来解决线程调度不均匀的问题。这种方法不仅适用于小型并发场景,还可以作为多线程编程的入门实践,用以理解线程调度与锁机制的基本原理。