赞
踩
现有一个需求如下:
有10000张火车票,每张票都有一个编号,同时有10个窗口对外售票,如何确保车票的正常售卖?
问题的解决办法都是从我们最最熟悉的角度思考。程序一,我们使用一个普通的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代替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();
- }
- }
- }
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;
- else
- System.out.println("销售了--" + s);
- }
- }).start();
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。