Java并发编程实战————售票问题

引言

现有一个需求如下:

有10000张火车票,每张票都有一个编号,同时有10个窗口对外售票,如何确保车票的正常售卖?

程序一:使用List

问题的解决办法都是从我们最最熟悉的角度思考。程序一,我们使用一个普通的List作为方案。

阅读以下代码,观察执行结果:

public class TicketSell_01 {static List<String> tickets = new ArrayList<>();static {for (int i = 0; i < 10000; i++)tickets.add("票编号: " + i);}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {while (tickets.size() > 0) {System.out.println("销售了--" + tickets.remove(0));}}).start();}}
}

输出结果如下,可以看到,编号0的车票被销售了两次。List不是同步容器,容器内的remove()等方法都无法做到原子性,因此会出现重复售票的问题,因此是不安全的:

程序二:使用Vector

以Vector代替List作为容器。区别是,Vector是同步容器,内部的方法都是同步的。但是下面的代码依然会存在问题。

虽然size()方法和remove()方法本身是原子性的,其他线程无法打断,但是在判断size和remove之间的部分依然会有线程交叉执行的可能,这样,虽然可以解决重复销售的问题,但是依然会导致:ArrayIndexOutOfBoundsException

public class TicketSell_02 {static Vector<String> tickets = new Vector<>();static {for (int i = 0; i < 10000; i++)tickets.add("票编号: " + i);}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {while (tickets.size() > 0) {try {TimeUnit.MILLISECONDS.sleep(5);} catch (Exception e) {e.printStackTrace();}System.out.println("销售了--" + tickets.remove(0));}}).start();}}
}

执行结果:

程序三:同步

使用synchronized进行线程同步,可以有效解决逻辑问题,但是很明显,这种方法的缺点就是效率低下。

public class TicketSell_03 {static List<String> tickets = new LinkedList<>();static {for (int i = 0; i < 10000; i++)tickets.add("票编号: " + i);}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {while (true) {synchronized (tickets) {if (tickets.size() == 0)break;System.out.println("销售了票--" + tickets.remove(0));}}}).start();}}
}

程序四:并发容器Queue

ConcurrentLinkedQueue是一个并发队列但凡并发容器,其内部的方法都保证是原子性的。下面的代码中poll()表示从队列的头部获得一个数据,当返回值为null时,代表这个队列已经没有值了。因为队列本身不允许存null值,否则会报空指针异常,因此当返回值为null时,一定表示队列已空(size() == 0)。队列的底层是使用一个叫做CompareAndSet(CAS)的技术实现的,不是加锁的实现,因此在高并发的情况下依然可以拥有很高的效率。

public class TicketSell_04 {static Queue<String> tickets = new ConcurrentLinkedQueue<>();static {for (int i = 0; i < 10000; i++)tickets.add("票编号: " + i);}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {while (true) {String s = tickets.poll();if (s == null)break;elseSystem.out.println("销售了--" + s);}}).start();}}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558917.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多线程相关知识

多线程相关知识 两个线程进行通信&#xff1a;通过等待&#xff08;wait&#xff09;唤醒&#xff08;notify&#xff09;机制 三个或三个以上线程进行通信&#xff1a;通过notifyAll&#xff08;&#xff09;方法 /* * 1. 在同步代码块中&#xff0c;用哪个对象锁&#xff0c…

Eclipse集成PyDev5.2.0开发插件

引言 在进行Python学习的时候&#xff0c;希望不使用IDLE进行开发&#xff0c;但是其他的IDE如PyCharm可能需要一段短暂时间的上手&#xff0c;因为开发过Java&#xff0c;所以使用能够集成到Eclipse上的PyDev插件进行开发应该会好一些。 但是在安装PyDev的时候发生了一些问题…

PostMan 四种常见的 POST 提交数据方式

HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 这几种。其中 POST 一般用来向服务端提交数据&#xff0c;本文主要讨论 POST 提交数据的几种方式。 协议规定 POST 提交的数据必须放在消息主体&#xff08;entity-body&#xff0…

史上最真实行业鄙视链

本文转载自菜鸟教程的微信公众号&#xff0c;原文链接&#xff1a;https://mp.weixin.qq.com/s/d9cdtq8y4Msq-_ZNof-iuw 引言 作为程序员的一份子&#xff0c;掌握好各个生态系统中的鄙视链&#xff0c;可以写出更加符合改变世界要求的代码。掌握了鄙视链&#xff0c;就掌握了…

权限验证框架Shiro

权限验证框架Shiro&#xff1a; Shiro简介 什么是Shiro&#xff1a; shiro是一个强大易用的Java安全框架&#xff0c;提供了认证&#xff0c;授权&#xff0c;加密&#xff0c;回话管理等功能&#xff1b; 认证&#xff08;Authentication&#xff09;&#xff1a;用户身份识别…

Mybatis从入门到精通上篇

Mybatis从入门到精通上篇&#xff1a; 学习过的持久层框架&#xff1a;DBUtils , Hibernate Mybatis就是类似于hibernate的orm持久层框架。 Mybatis介绍&#xff1a; Mybatis是面向sql的持久层框架&#xff0c;他封装了jdbc访问数据库的过程&#xff0c;我们开发&#xff0c;只…

Eclipse使用————Working Set工作集

引言 经常看到在设置项目的时候&#xff0c;如导入项目&#xff0c;新建项目等看到对话框的下方有一个“add to working set”复选框&#xff0c;为了弄清这个working set&#xff0c;我们就来好好了解一下Eclipse 的working set功能。 Working Set&#xff1f; Eclipse中通…

Mybatis从入门到精通下篇

Mybatis从入门到精通下篇&#xff1a; 输入类型&#xff1a; 输出类型&#xff1a; ResultMap&#xff1a; 动态sql&#xff1a; if标签&#xff1a; where标签&#xff1a; sql片段&#xff1a; foreach标签&#xff1a; 关联查询&#xff1a; 以订单作为主体&#xff1a; 一…

爱上进制转换练习

引言 对于可能接触到通讯行业或是物联网的开发工作者&#xff0c;一般会面对十进制、二进制、十六进制的转换工作&#xff0c;不仅仅是体现在代码上&#xff0c;有时候也需要用肉眼来进行快速的转化&#xff0c;以获取协议指令中的信息。 今天通过简单的整理&#xff0c;特此…

Sprint Boot————@Qualifier、@Primary

引言 使用Autowired自动注入时&#xff1a; 如果注入的接口有多个实现类&#xff0c;如下所示&#xff1a; 那么如果不指定具体是哪个实现类的Bean&#xff0c;在Spring Boot启动时就会发生异常&#xff08;下图请点击查看&#xff09;&#xff1a; 异常的描述信息非常简单&am…

SpringMVC教程上篇

SpringMVC教程上篇 SpringMVC优势&#xff1a; SpringMVC代码执行流程&#xff1a; 框架结构&#xff1a; 架构流程&#xff1a; 组件说明&#xff1a; SpringMVC与Mybatis整合 ! 效果: 开发流程&#xff1a;

Eclipse使用————生成Get/Set、toString快捷键(不使用鼠标)

引言 除了鼠标右键空白处—>source选择我们需要的操作之外是否还有更快捷的不需要鼠标的操作呢&#xff1f; 如何快速的通过键盘来生成get、set方法&#xff1f;如何快速的通过键盘生成toString方法&#xff1f;如何快速的通过键盘生成需要实现的父类方法呢&#xff1f; …

SpringMVC教程下篇

SpringMVC教程下篇 内容包括&#xff1a; 绑定数组&#xff1a; 将表单数据绑定到list&#xff1a; RequestMapping注解的三种用法&#xff1a; Controller方法返回值&#xff1a; 乱码问题总结 异常处理&#xff1a; 照片上传&#xff1a; RESTFUL支持&#xff…

Spring Boot面试杀手锏————自动配置原理

引言 不论在工作中&#xff0c;亦或是求职面试&#xff0c;Spring Boot已经成为我们必知必会的技能项。除了某些老旧的政府项目或金融项目持有观望态度外&#xff0c;如今的各行各业都在飞速的拥抱这个已经不是很新的Spring启动框架。 当然&#xff0c;作为Spring Boot的精髓…

为什么要坚持写博客

引言 断断续续地写博客已经有一段时间了&#xff0c;作为一个Java中级开发工程师&#xff0c;工作了三年多也算渐渐入了门。不得不说&#xff0c;博客给我的改变是非常大的&#xff0c;那么作为一个技术人员&#xff0c;为什么我觉得必须要坚持写博客&#xff1f;下面&#xf…

Spring Boot——@ConfigurationProperties与@Value的区别

引言 Spring Boot从配置文件中取值的方式有两种&#xff0c;一种是批量注入ConfigurationProperties&#xff0c;另一种是单独注入Value。 它们之间除了批量与单独取值的区别之外&#xff0c;还存在着其他一些使用方式&#xff0c;本篇博客将详细讲解这两种注解之间的区别和使…

Spring Boot —— YAML配置文件

引言 首先&#xff0c;YAML并不是仅仅可以使用在Java项目中&#xff0c;它是一种类似于json结构的标记语言&#xff0c;可以为所有的编程语言服务。它强调更直观的层级表示&#xff0c;比较适合描述配置文件中的层级关系。 Spring Boot可以识别后缀名为".properties&quo…

centos7下docker启动失败解决

centos7下docker启动失败解决 docker安装成功却启动失败&#xff0c;查看docker服务&#xff0c;systemctl status docker.service, 服务日志提示Failed to start Docker Application Container Engine.如下图所示&#xff1a; 解决方法&#xff0c;修改docker文件&#xff0…

Java并发编程实战————Semaphore信号量的使用浅析

引言 本篇博客讲解《Java并发编程实战》中的同步工具类&#xff1a;信号量 的使用和理解。 从概念、含义入手&#xff0c;突出重点&#xff0c;配以代码实例及讲解&#xff0c;并以生活中的案例做类比加强记忆。 什么是信号量 Java中的同步工具类信号量即计数信号量&#x…

JavaCard概述

什么是JavaCard JavaCard&#xff0c;即Java智能卡。以智能卡硬件系统为基础&#xff0c;通过软件的方式构造一个支持Java程序下载、安装、运行的软/硬件系统。由于引入了虚拟机技术&#xff0c;JavaCard具备硬件无关性&#xff0c;即智能卡应用程序开发与智能卡硬件系统相分离…