zoukankan      html  css  js  c++  java
  • 多线程售票模型

    上次Java实验课就写了一次,但是比较简单,这里再好好写一下

    这里用集合类LinkList来存储车票

    注意线程是抢占式时的,虽然在出票的时候是一个个出的,但是在Sysout.out.println()的时候被其他线程抢占,所以输出可能不是顺序的,这里用sleep方法之后就顺序输出了,为什么?

    task1在进入同步快之后,task2需要进行等待,
    当task1结束同步块后,task2进入同步块,此时task2需要slee1秒钟才能结束同步快,而此时task1已经开始准备进行打印了。虽然理论上说task1依然有可能没有抢到cpu而无法打印,但是task2需要等待1秒钟,在1秒钟之内task1都抢不到cpu的概率实在太低,基本可以忽略,这样就出现了顺序输出的情况。

    一般不用加sleep,因为后台出票的时候确实是一个个按照顺序出的。只是在打印的时候抢占了。

    package com.ticket.thread;
    
    import java.util.LinkedList;
    
    import com.sun.org.apache.xalan.internal.xsltc.compiler.sym;
    
    /**
     * 带销售的车票
     * 
     * @author hwyou
     *
     */
    public class Ticket {
        
        // 所有的车票(容器)
        LinkedList<Integer> list = new LinkedList<>();
        
        /**
         * 
         * @param size
         *            车票的数量
         */
        public Ticket(int size) {
            for(int i = 1; i <= size; i ++) {
                list.add(i);
            }
            System.out.println(list);
        }
        
        //同步关键字,其他线程在当前线程没有结束之前是不能访问的
        //"试衣间",对当前衣服在试衣间试衣服的时候其他人不能访问
        public  int sell() throws NoTicketException {
    //        int t = list.getFirst();
            
            //异常,对正常流程的中断,这里最好是放在同步块里
            if(list.size()==0) {
                throw new NoTicketException();
            }
            
            synchronized(list) {
                int t = list.removeFirst();    
                
            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return t;
            }
            
        }
        
        public void show() {
            System.out.println("剩余" + list);
        }
    
        
    }

    这里加synchronized关键字,同步化之后,避免了多个线程访问同一张票,上个写法没有很体现出来,我们这样写,再用sleep放大,就提现了synchronized重要了。

    有问题的写法:

    package com.ticket.thread;
    
    import java.util.LinkedList;
    
    import com.sun.org.apache.xalan.internal.xsltc.compiler.sym;
    
    /**
     * 带销售的车票
     * 
     * @author hwyou
     *
     */
    public class Ticket {
        
        // 所有的车票(容器)
        LinkedList<Integer> list = new LinkedList<>();
        
        /**
         * 
         * @param size
         *            车票的数量
         */
        public Ticket(int size) {
            for(int i = 1; i <= size; i ++) {
                list.add(i);
            }
            System.out.println(list);
        }
        
        // 同步关键字,其他线程在当前线程没有结束之前是不能访问的
        // "试衣间",对当前衣服在试衣间试衣服的时候其他人不能访问
        public int sell() throws NoTicketException {
            int t = list.getFirst();
    
            // 异常,对正常流程的中断
            if (list.size() == 0) {
                throw new NoTicketException();
            }
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            list.removeFirst();
    
            return t;
    
        }
        
        public void show() {
            System.out.println("剩余" + list);
        }
    
        
    }

    因为在实际项目中,取票和出票中间确实可能有其他代码,其他操作,没有同步,就可能出这种错误

    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
    pool-1-thread-1开放
    pool-1-thread-2开放
    pool-1-thread-3开放
    pool-1-thread-4开放
    pool-1-thread-5开放
    pool-1-thread-1售出1
    pool-1-thread-4售出1
    pool-1-thread-2售出1
    pool-1-thread-3售出1
    pool-1-thread-5售出1
    pool-1-thread-3售出4
    pool-1-thread-1售出4
    pool-1-thread-4售出4
    pool-1-thread-2售出4
    pool-1-thread-5售出4
    pool-1-thread-1售出6
    pool-1-thread-2售出6
    pool-1-thread-3售出6
    pool-1-thread-4售出6
    pool-1-thread-5售出7
    pool-1-thread-3售出10
    pool-1-thread-2售出10
    pool-1-thread-4售出10
    pool-1-thread-1售出10
    pool-1-thread-5售出11
    pool-1-thread-1售出14
    pool-1-thread-3售出14
    pool-1-thread-2售出14
    pool-1-thread-4售出14
    pool-1-thread-5售出15
    pool-1-thread-5售罄
    pool-1-thread-4售出19
    pool-1-thread-1售罄
    pool-1-thread-3售罄
    pool-1-thread-2售罄
    pool-1-thread-4售罄

    一些票因为线程访问同一张票也减减,所以直接在后台就没了,前端打印自然也没有显示

    非线程池写法:

    package com.ticket.thread;
    /**
     * 自定义一个线程(售票窗口)
     * @author Administrator
     *
     */
    public class TicketOffice extends Thread {
        /**
         * 要销售的车票(引用)
         */
        Ticket ticket;
        
        /**
         * 创建(初始化)售票窗口
         * @param ticket
         *             待销售的车票
         * @param name
         *             窗口名称
         */
        public TicketOffice(Ticket ticket, String name) {
            this.ticket = ticket;
            setName(name);
        }
        
        @Override
        public void run() {
            // TODO Auto-generated method stub
            super.run();
            
            //售票
            System.out.println(getName()+ "开放");
            while (true) {
                try {
                    int t = ticket.sell();
                    System.out.println(getName() + "售出:" + t);
                } catch (NoTicketException e) {
                    // TODO Auto-generated catch block
    //                e.printStackTrace();
                    System.out.println(getName()+"售罄");
                    break;
                }
            }
            System.out.println("关闭");
        }
    }

    线程池写法:

    package com.ticket.thread;
    
    public class SaleTask implements Runnable{
        Ticket ticket;
        
        public SaleTask(Ticket ticket) {
            this.ticket = ticket;
        }
        
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"开放");
            while(true) {
                try {
                    int t = ticket.sell();
                    System.out.println(Thread.currentThread().getName() + "售出" + t);
                } catch (Exception e) {
                    System.out.println(Thread.currentThread().getName() +"售罄");
                    break;
                }
            }
        }
        
    }

    自定义异常类:

    package com.ticket.thread;
    /**
     * 消息对象(信号)
     * @author Administrator
     *
     */
    public class NoTicketException extends Exception{
            
    }

    入口:

    package com.ticket.thread;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class App {
        public static void main(String[] args) {
            //车票
            Ticket ticket = new Ticket(20);
            
    //        //创建窗口
    //        TicketOffice office1 = new TicketOffice(ticket, "窗口一");
    //        TicketOffice office2 = new TicketOffice(ticket, "窗口二");
    //        TicketOffice office3 = new TicketOffice(ticket, "窗口三");
            //ExecutorService是一个对象,容器,装线程的池子
            ExecutorService pool = Executors.newFixedThreadPool(5);
            
            for(int i = 0; i < 5; i++) {
                pool.execute(new SaleTask(ticket));
            }
            
            pool.shutdown();
    //        office1.start();
    //        office2.start();
    //        office3.start();
        }
    }
  • 相关阅读:
    菜鸟刷题路:剑指 Offer 09. 用两个栈实现队列
    python 全栈开发,Day40(进程间通信(队列和管道),进程间的数据共享Manager,进程池Pool)
    python 全栈开发,Day39(进程同步控制(锁,信号量,事件),进程间通信(队列,生产者消费者模型))
    python 全栈开发,Day38(在python程序中的进程操作,multiprocess.Process模块)
    python 全栈开发,Day37(操作系统的发展史)
    python 全栈开发,Day36(作业讲解(大文件下载以及进度条展示),socket的更多方法介绍,验证客户端链接的合法性hmac,socketserver)
    python 全栈开发,Day35(TCP协议 粘包现象 和解决方案)
    python 全栈开发,Day34(基于UDP协议的socket)
    python 全栈开发,Day33(tcp协议和udp协议,互联网协议与osi模型,socket概念,套接字(socket)初使用)
    python 全栈开发,Day32(知识回顾,网络编程基础)
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/7087425.html
Copyright © 2011-2022 走看看