不同多线程创建方式下,售票的安全用例

博客介绍了多种创建多线程及保证数据同步的方法,包括实现Runnable接口、使用定长线程池、继承Thread类、利用FutureTask和Callable等,还提及使用synchronized、Lock类及Lock.tryLock()保证数据安全,最后给出线程及线程池参考链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

实现Runnable接口创建多线程,synchronized保证数据同步:

    @Test
    public void runnableSale() {
        int threadNum = 4;
        //@Test不同于main方法,需要下面的同步工具等待子线程执行完成
        //启用同步工具类对子线程计数,为了让主线程启动子线程后,等待子线程完成对应的工作
        CountDownLatch countDownLatch = new CountDownLatch(threadNum);
        Set<Integer> set=new HashSet<>();
        //开启子线程,为了让他们的锁一致,多个线程需要同一个对象
        MyTask myTask = new MyTask(countDownLatch,set);
        for (int i = 0; i < threadNum; i++) {
            new Thread(myTask).start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


class MyTask implements Runnable {
    private final Set<Integer> set;
    private CountDownLatch countDownLatch;
    private int ticket = 100;

    public MyTask(CountDownLatch countDownLatch, Set<Integer> set) {
        this.countDownLatch = countDownLatch;
        this.set=set;
    }

    @Override
    public void run() {
        while (ticket > 0) {
            sale();
            try {
                Thread.currentThread().sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        countDownLatch.countDown();
    }

    //两种synchronized方式都可以保证同步
    public synchronized void sale() {
        System.out.println(Thread.currentThread().getName() + "::::::::出售第" + ticket);
        if(set.contains(ticket)){
            System.err.println("重复出票:"+ticket);
        }else{
            set.add(ticket);
        }
        ticket--;
    }

    public void sale1() {
        synchronized (MyTask.class) {
            System.out.println(Thread.currentThread().getName() + "::::::::出售第" + ticket--);
        }
    }
}

使用定长线程池获取线程,Lock类的方式保证数据同步:

 public static void main(String[] args) {
        //定长线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(20);
        MyTaskInThreadPool myTaskInThreadPool = new MyTaskInThreadPool();
        for (int i = 0; i < 100; i++) {
            fixedThreadPool.execute(myTaskInThreadPool);
        }

    }

    static class MyTaskInThreadPool implements Runnable{
        private int ticket = 100;
        private Lock ticketLock =new ReentrantLock();

        //买票方法
        public  void sale() {
            //重入锁
            ticketLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "::::::::出售第" + ticket);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                --ticket;
            }finally {
                //保证释放锁
                ticketLock.unlock();
            }

        }

        @Override
        public void run() {
            sale();
        }
    }

使用继承类Thread创建多线程,使用Lock.tryLock()保证数据安全:

    public static void main(String[] args) {

        Lock lock = new ReentrantLock();//必须让所有线程使用同一个lock
        Set<Integer> set=new HashSet<>();
        for (int i = 0; i < 10; i++) {
            new MyTaskExtends(lock, set).start();//开启10个线程执行
        }

    }
class MyTaskExtends extends Thread {
    private final Set<Integer> set;
//    private int ticket=100;//创建一个对象就有100张票,相当于每开启一个线程,都会从第100张票开始出售。改用下面的做法
    private static int ticket=100;//静态变量是所有对象都共享,100张票。几个线程,卖的都是一个票。
    private Lock lock;

    public MyTaskExtends(Lock lock, Set<Integer> set) {
        this.lock = lock;
        this.set=set;
    }

    @Override
    public void run() {
//        boolean b = lock.tryLock();
        while (ticket > 0) {
            boolean b = false;
            try {
                b = lock.tryLock(10, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (ticket <= 0) {
                System.out.println("ticket<0,线程返回"+set.toString());
                return;
            }
            if (b) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":::::出售票:" + ticket);
                    //利用set集合判断是否重复售票
                    if(set.contains(ticket)){
                        System.err.println("重复售票:"+ticket);
                    }else {
                        set.add(ticket);
                        System.out.println("添加:"+ticket);
                    }
                    ticket--;
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {//释放锁时,必须保证锁是获取到了的
                    System.out.println(Thread.currentThread().getName() + ":::::释放锁");
                    lock.unlock();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + ":::::没有获取到锁");
            }
        }
    }
}

使用FutureTask和Callable创建有返回值的子线程 

 public static void main(String[] args) {
        //使用Lambda表达式创建Callable对象
        //使用FutureTask类来包装Callable对象
        Integer taskNo=100;
        FutureTask<Object> future = new FutureTask<>((Callable<Object>) () -> {
            return "this is callable";
        });

        //实质上还是以Callable对象来创建并启动线程
        new Thread(future,"有返回值的线程").start();

        try {
            System.out.println("子线程的返回值:" + future.get());//get()方法会阻塞,直到子线程执行结束才返回
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

实现Callable接口创建有返回值的线程,使用synchronized保证数据安全

 public static void main(String[] args) {
        Set<Integer> set=new HashSet<>();
        MyTaskFuture myTaskFuture = new MyTaskFuture(set);
        List<FutureTask> list=new ArrayList<>(50);
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);//创建10个执行人
        for (int i = 0; i < 110; i++) {//模拟110个并发量,异步提交执行抢票
            FutureTask ft=new FutureTask(myTaskFuture);//添加执行的任务
            list.add(ft);
            fixedThreadPool.submit(ft);//提交执行任务给10个执行人
        }
        System.out.println("主线程异步提交执行计算,去做其他事情!");
        System.out.println("主线程阻塞,等待异步执行完成的数据");
        for (FutureTask ft : list) {
            try {
                String res = String.valueOf(ft.get());
                System.out.println(res);
                if(res.contains("error")){
                    System.err.println(res);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        // 关闭线程池
        fixedThreadPool.shutdown();
    }

class MyTaskFuture implements Callable{
    private final Set<Integer> set;
    int ticket=100;

    public MyTaskFuture(Set<Integer> set) {
        this.set = set;
    }

    @Override
    public String call() throws Exception {
        while (ticket>0){
            synchronized (MyTaskFuture.class){
                if(ticket>0){
                    if(set.contains(ticket)){
//                        System.out.println("重复售票:"+ticket);
                        return Thread.currentThread().getName()+"error重复售票"+ticket;
                    }else {
                        set.add(ticket);
                    }
//                    System.out.println(Thread.currentThread().getName()+":::::售票:"+ticket);
                    return Thread.currentThread().getName()+"成功售票:"+ticket--;
                }else {
                    return Thread.currentThread().getName()+"售票结束,你没抢到票!";
                }
            }
        }
        return Thread.currentThread().getName()+"售票结束,你没抢到票";
    }

}

 线程及线程池参考:https://blog.youkuaiyun.com/BigBug_500/article/details/90294569

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值