首页 > 代码库 > 多线程售票模型

多线程售票模型

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

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

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

一般不用加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();    }}

 

多线程售票模型