一、场景如下:
有一百张票,创建10个线程进行模拟抢票,用不同方式创建多线程。
二、继承Thread类
public class ThreadDemo {
//总优惠券数量
private static int count = 100;
//创建锁对象
private static final Object lock = new Object();
private static boolean flag = false;
public static void main(String[] args) {
//创建10个线程
for (int i = 0; i < 10; i++) {
new MyThread("线程" + i).start();
}
}
//抢券线程类
static class MyThread extends Thread {
public MyThread(String name) {
super(name);//设置线程名称
}
//重写run方法,开启线程后要执行的业务逻辑
@Override
public void run() {
while (true) {
//加锁
synchronized (lock) {
if (flag) {
break;
}
if (count <= 0) {
flag = true;
System.out.println("券已抢完!");
break;//券抢完时退出循环
}
//模拟抢券过程
count--;
System.out.printf("%s 抢到第 %d 张券,剩余 %d 张券\n",
Thread.currentThread().getName(),
100 - count,
count);
// 关键:释放锁后给其他线程机会
try {
// 增加短暂停顿(毫秒)
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
三、实现Runnable接口
public class RunnableDemo {
//定义券数量
private static int count = 100;
//定义锁对象
private static final Object lock = new Object();
//定义标志位
private static boolean flag = false;
public static void main(String[] args) {
MyRunnable task = new MyRunnable();
//创建10个线程
for (int i = 0; i < 10; i++) {
new Thread(task, "线程" + i).start();//如果没有传task,那么就会执行默认的run方法
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
while (true) {
//加锁
synchronized (lock) {
if (flag) {
break;
}
//如果券抢完了
if (count <= 0) {
System.out.println("当前券已抢完");
flag = true;
break;
}
//开始抢券
count--;
System.out.printf("%s 抢到第 %d 张券,剩余 %d 张券\n",
Thread.currentThread().getName(),
100 - count,
count);
}
//解锁
//抢完券休息一下,让其他线程有机会抢
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
四、使用线程池+CAS
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolDemo {
// 券数量(使用原子类更高效)
private static final AtomicInteger count = new AtomicInteger(100);
// 结束标志 - 改为volatile保证可见性
private static volatile boolean flag = false;
// 线程池配置调整
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, // 核心线程数
10, // 最大线程数
60L, // 改为60秒空闲时间(更符合实际)
TimeUnit.SECONDS,
new LinkedBlockingQueue<>()
);
public static void main(String[] args) throws InterruptedException {
// 提交抢券任务
for (int i = 0; i < 10; i++) {
executor.submit(new MyTask("线程" + i));
}
// 关闭线程池
executor.shutdown();
// 等待所有任务完成(最长等待30秒)
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
System.err.println("部分任务仍在执行!强制终止...");
executor.shutdownNow();
}
System.out.println("所有任务已完成!");
}
//线程任务
static class MyTask implements Runnable {
private final String taskName; // 改为final
MyTask(String name) {
this.taskName = name; // 使用传入的名称
}
@Override
public void run() {
while (!flag) { // 改为检查flag,避免死循环
// 获取当前券数
int curCount = count.get();
// 如果券没了 - 需要正确结束处理
if (curCount <= 0) {
// 安全设置结束标志
synchronized (ThreadPoolDemo.class) {
if (!flag) {
flag = true;
System.out.println("***** 券已经抢完了 *****"); // 只打印一次
}
}
break;
}
// CAS操作
boolean success = count.compareAndSet(curCount, curCount - 1);
if (success) {
// 正确计算剩余和已抢数量
int couponNumber = 101 - curCount;
int currentRemaining = curCount - 1;
System.out.printf("%s 抢到第 %d 张券,剩余 %d 张券\n",
taskName,
couponNumber, // 修正计算逻辑
currentRemaining);
// 每次抢券后短暂休息
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// 正确处理中断异常
System.out.println(taskName + "被中断!");
Thread.currentThread().interrupt(); // 恢复中断状态
break;
}
} else {
// CAS失败时短暂休眠,减少CPU竞争
try {
Thread.sleep(1);
} catch (InterruptedException e) {
System.out.println(taskName + "被中断!");
Thread.currentThread().interrupt();
break;
}
}
}
}
}
}
五、模拟交替抢票
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
//多线程交替抢票
//1.我们定义一个当前允许执行的线程的序号(例如从0到9)。
//2.每个线程在抢票前,先判断当前轮到自己执行了吗?如果不是,则等待(使用wait)。
//3.如果是自己执行,则执行抢票操作,然后更新下一个允许执行的线程序号(按0-9循环),并唤醒所有等待的线程(使用notifyAll)。
//4.由于是轮流执行,我们需要确保每次只有一个线程执行抢票操作,因此抢票操作本身就不需要额外的同步了,因为轮流机制已经是同步的。
public class OrderedCouponDemo {
// 券数量
private static final AtomicInteger couponCount = new AtomicInteger(100);
// 当前允许执行的线程索引
private static int currentThreadIndex = 0;
// 协调锁
private static final ReentrantLock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
// 全局结束标志
private static volatile boolean allDone = false;
//线程池
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, //核心线程池
10, //最大线程池
60L, //空闲时间
TimeUnit.SECONDS,
new LinkedBlockingDeque<>()
);
public static void main(String[] args) throws InterruptedException {
// 1.提交10个抢券任务
for (int i = 0; i < 10; i++) {
final int threadIndex = i; // 线程的固定索引
executor.submit(() -> runTask(threadIndex));
}
// 2.关闭线程池
executor.shutdown();
// 3.等待所有任务完成(30秒超时)
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
System.err.println("部分任务仍在执行!强制终止...");
// 设置全局结束标志并唤醒所有线程
lock.lock();
try {
allDone = true;
condition.signalAll();
} finally {
lock.unlock();
}
// 再次等待终止
executor.awaitTermination(5, TimeUnit.SECONDS);
executor.shutdownNow();
}
}
/**
* 抢券任务核心逻辑
* @param threadIndex 线程固定索引
*/
private static void runTask(int threadIndex) {
while (!allDone) { // 检查全局结束标志
lock.lock();
try {
// 1.1 检查是否全局结束
if (allDone) {
break;
}
// 1.2 等待轮到自己的轮次(带超时)
while (!allDone && threadIndex != currentThreadIndex) {
condition.await(1, TimeUnit.SECONDS);
if (allDone) break; // 再次检查结束标志
}
// 1.3 检查是否结束或券已抢完
if (allDone || couponCount.get() <= 0) {
if (!allDone) { // 如果是券抢完触发结束
System.out.println("***** 券已全部抢光! *****");
allDone = true; // 设置全局结束标志
}
break;
}
// 1.4 执行抢券操作
int curCount = couponCount.getAndDecrement();
int couponNumber = 101 - curCount;
System.out.printf("%s 抢到第 %d 张券,剩余 %d 张券\n",
Thread.currentThread().getName(),
couponNumber,
curCount - 1);
// 1.5 更新轮次索引(轮转)
currentThreadIndex = (currentThreadIndex + 1) % 10;
// 1.6 唤醒所有等待的线程
condition.signalAll();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
} finally {
lock.unlock();
}
// 1.7 模拟处理延迟
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
多线程抢票代码示例解析
1360

被折叠的 条评论
为什么被折叠?



