多线程并发编程-模拟抢票代码示例

多线程抢票代码示例解析

一、场景如下:

有一百张票,创建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;
            }
        }
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值