OMG,由于是比较初级,很少写到关于线程的东西,真是无从下手,能否说的详细点啊,请指条路~

解决方案 »

  1.   

    如果仅仅是解决这个问题,碰线程纯属多次一举,用for循环就可以搞定的事情还要用多线程,多线程的好处不用多说,但是多线程带来的问题是同步控制和同步所浪费的时间,如果不是为了秀技术,能用单线程还是不要用多线程的好。
      

  2.   

    说下我的我的思路希望能帮到你:
    step1,把收银台何顾客都封装成对象。
    step2,收银台对象中:list集合用来存储顾客对象。
            顾客对象中:有一个time属性,用来存储结账的时间。
                        有一个chose()方法,用来选择收银台。
    step3,创建N个收银台,M个顾客。设置好属性。把这些顾客安排到收银台的list中。
    step4,计算每个收银台list中顾客的结账时间,最后把N个收银台的结账时间相加。完成
      

  3.   

    难点就是chose()方法的书写
      

  4.   

    去看看Think in Java就很多了
      

  5.   


    谢谢你的思路,很好。
    我有几个问题
    1. 需不需要监测每个时间,收银台的状态。因为每个顾客都有到达的时间,这时应该进行chose哪个收银台。问题是,如何知道每个收银台每分钟的状态。很有可能每分钟都有顾客到达等待结账。
    2. 顾客是不是不需要有个 list 了,直接chose哪个收银台就好?
    3, 如何显示N个收银台的同步性啊,如果不用线程的话。这个几个收银台是同时在给顾客结账的。谢谢~
      

  6.   

    我也不想用线程,因为对线程不是很熟悉。
    那请问不用线程怎么实现啊?顾客如何才能知道每个收银台的状态,有多少人离开,多少人在结账啊?谢谢不用线程怎么实现并发?
    难不成你多个收银台还不能同时结帐?
    运用线程后,如何去查看每一个收银台的状态,比如队里有多少个人,还有如何查看队里最后一个人的商品数?
    能不能给点具体点思路和建议啊~一个收银台对应一个list和一个线程就好了啊,线程相当于现实中的收银员,list相当于收银台前的队就好了。你有几个收银台就开几个线程。这么简单的问题怎么会给整那么复杂
      

  7.   

    list放customer对象,线程检查list中第一个costomer,根据他的商品数sleep相应的时间,然后移除这个customer,处理下一个customer。处理过程跟现实生活一模一样。都不明白难点在哪?
      

  8.   

    原来大神在这里啊
    由于本人是菜鸟级别,希望不要被大神鄙视我的难点是
    1. 假如一个顾客在t=10 的时候到达,他如何检查 这N个收银台中每一个收银台(1, 2, 3 ...n) 在此刻 队伍里的人数, 然后再去选择去不同的收银台。
    2. 有N个收银台,M个顾客。 在main函数中,需要用 for 循环 分别实例化这N个收银台吗? 我想的是因为顾客要去check 每一个收银台的 在他到达时的状态,如果只是实例化一次,不知道该如何实现。难点主要就是 顾客如何检查每个收银台的状态,再去做决定进入哪个队列。谢谢大神~
      

  9.   

    eth90-0ieropkoiperw0igjopi9i-0fweokj0fjkoewdkop0-iewokprf-0ewgt0-erkyp
      

  10.   

    这边就“收银台”和“顾客”,做两个抽象吧:
    收银台的建模思路:收银台有普通和培训之分,因此存在一个属性type(可以做成int型,也可为枚举);当前收银台可以接收顾客排队,因此需要一个Queue<顾客> customQueue属性,存放正则排队的顾客;顾客会判断当前收银台最后一个顾客商品数,因此需要一个numOfGoods属性;判断收银台排队人数,通过customQueue.size()即可;为了区分,再增加一个收银台编号,使用字符串。
    这样,收银台的数据模型可以是这样的:/**
     * 收银台的抽象
     */
    public class CashDesk {    // 收银台类型
        private DeskType        type;    // 编号
        private String          name;    // 排队的顾客
        private Queue<Customer> customQueue;    // 队列尾的货物数
        private int             numOfGoods;    // 构造方法,必须输入收银台种类和编号
        public CashDesk(DeskType type, String name) {
            this.type = type;
            this.name = name;        // 顾客队列、队尾货物数初始化
            customQueue = new LinkedBlockingQueue<Customer>();
            numOfGoods = 0;
        }    // 顾客来了的方法
        public void customersComing(Customer... customers) {
            // 不能为空
            if (null == customers || 0 == customers.length) {
                System.out.println("没有顾客结账...");
                return;
            }
            // 加入队列
            customQueue.addAll(Arrays.asList(customers));
            // 修改队尾顾客货物数
            numOfGoods = customers[customers.length - 1].getNumOfGoods();
        }    // 处理队列中第一位顾客的结账
        public void processFirstCustomerCash() {
            if (customQueue.isEmpty()) {
                return;
            }        // 获取第一个顾客
            Customer firstOne = customQueue.poll();
            // 处理他的货物
            do {
                try {
                    // 收银台分类
                    switch (type) {
                    case Normal: {
                        Thread.sleep(60000);// 1min
                        break;
                    }
                    case Practice: {
                        Thread.sleep(120000);// 2min
                    }
                    }
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }            // 打印
                System.out.println(name + ":" + "搞定顾客" + firstOne.getNumber() + "的1个货物。");            // 若当前顾客为队列最后一个顾客,则需要实时更新队尾货物数
                if (customQueue.isEmpty()) {
                    numOfGoods = firstOne.getNumOfGoods();
                }
            } while (firstOne.deelNumOfGoods());        // 若队列空了,则修改最后顾客货物数
            if (customQueue.isEmpty()) {
                numOfGoods = 0;
            }
        }    // 判断排队人数是否为空
        public boolean queueIsEmpty() {
            return customQueue.isEmpty();
        }    // 获取当前排队人数
        public int getQueueSize() {
            return customQueue.size();
        }    // getter、setter方法
        public int getNumOfGoods() {
            return numOfGoods;
        }    public DeskType getType() {
            return type;
        }    public String getName() {
            return name;
        }    // 私有枚举,区分收银台种类
        public static enum DeskType {
            Normal, Practice
        }
    }顾客的建模思路:顾客也有分类,存在一个属性type(同上);顾客持有的货物数,numOfGoods属性;实现如下:/**
     * 顾客的抽象
     */
    public class Customer {    // 顾客种类
        private CustomType type;    // 顾客编号
        private int        number;    // 持有货物数
        private int        numOfGoods;    // 构造方法,必须传入顾客种类、编号和货物数
        public Customer(CustomType type, int number, int numOfGoods) {
            // 货物数不能小于等于0
            if (numOfGoods <= 0) {
                throw new IllegalArgumentException("货物数不能小于0!");
            }
            this.type = type;
            this.numOfGoods = numOfGoods;
            this.number = number;
        }    // 持有货物数-1
        public boolean deelNumOfGoods() {
            numOfGoods--;
            return numOfGoods > 0;
        }    public int getNumOfGoods() {
            return numOfGoods;
        }    public CustomType getType() {
            return type;
        }    public int getNumber() {
            return number;
        }    // 顾客种类枚举
        public enum CustomType {
            A, B
        }
    }参考实际收银台的运行,多线程并发执行是比较好的选择。这里的多线程不妨假设为收银员的收银处理,实现如下:/**
     * 收银员收银处理线程
     */
    public class CashProcessor implements Runnable {    // 收银台
        private CashDesk cashDesk;
        
        // 构造方法需要初始化收银台
        public CashProcessor(CashDesk cashDesk) {
            if (null == cashDesk) {
                throw new IllegalArgumentException("收银台不能为空!");
            }
            this.cashDesk = cashDesk;
        }    /**
         * 匹配条件4
         * 顾客来了,考虑到可能会有多个顾客,使用数组参数
         */
        public void customersComing(Customer... customers) {
            if (null == customers || 0 == customers.length) {
                System.out.println("没有顾客前来结账...");
                return;
            }        // 为了处理这些顾客排队问题,先进行排序处理// 排序的算法这里就简单的冒泡了
            Customer tmp;
            for (int i = 0; i < customers.length; i++) {
                for (int j = customers.length; j > i; j--) {
                    // 如果两个顾客的在同一时间到达 收银台, 商品数少的顾客先结账, 如果商品数一样,则A类顾客先于B类
                    if (customers[j].getNumOfGoods() < customers[j - 1].getNumOfGoods()
                            || customers[j].getNumOfGoods() == customers[j - 1].getNumOfGoods()
                            && customers[j].getType() == CustomType.A
                            && customers[j - 1].getType() == CustomType.B) {
                        tmp = customers[j];
                        customers[j] = customers[j - 1];
                        customers[j - 1] = tmp;
                    }
                }
            }        // 将排好队的顾客加入收银台顾客队列
            cashDesk.customersComing(customers);
            
            // 顾客来了,唤醒当前休息的收银台处理线程
            this.notify();
        }    @Override
        public void run() {
            // 收银台处理永不停止
            for(;;){
                // 没有顾客,等待
                if(cashDesk.queueIsEmpty()){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                // 被唤醒后,处理队伍头的顾客买单
                cashDesk.processFirstCustomerCash();
            }
        }
        
        // 获取收银台当前排队数
        public int getCashDeskQueueSize(){
            return cashDesk.getQueueSize();
        }
        
        // 获取收银台最后一个人货物数
        public int getLastCustmGoodsNum(){
            return cashDesk.getNumOfGoods();
        }
    }此外,还需要一个辅助的工具类,实现一组顾客到底加入到哪个收银员队列,实现如下:/**
     * 提供辅助方法,帮助实现将,一组顾客根据当前收银台排队情况,选择不同的收银台
     */
    public class DeskChoserHelper {    /**
         * 参照条件2 新来的一组顾客根据当前收银台排队情况,选择不同的收银台
         */
        public static void choseCashDesk(List<Customer> customers, List<CashProcessor> cashProcessors) {
            if (null == customers || 0 == customers.size()) {
                System.out.println("没有顾客前来结账...");
                return;
            }
            if (null == cashProcessors || 0 == cashProcessors.size()) {
                System.out.println("没有可用的收银处理器...");
                return;
            }        // 若收银台排队处理器只有一个,则直接调用
            if (1 == cashProcessors.size()) {
                cashProcessors.get(0)
                        .customersComing(customers.toArray(new Customer[customers.size()]));
                return;
            }        // 按照AB两类顾客的喜好,获得符合要求的两个收银台
            CashProcessor cashProcessorA = getCashProcessorA(cashProcessors);
            CashProcessor cashProcessorB = getCashProcessorB(cashProcessors);        // 若AB两个最优选择一样,则顾客无需分组
            if (cashProcessorA == cashProcessorB) {
                cashProcessorA.customersComing(customers.toArray(new Customer[customers.size()]));
                return;
            }        // 将顾客分成A/B两类
            List<Customer> customerA = new ArrayList<Customer>();
            List<Customer> customerB = new ArrayList<Customer>();
            for (Customer tmp : customers) {
                if (tmp.getType() == CustomType.A) {
                    customerA.add(tmp);
                } else {
                    customerB.add(tmp);
                }
            }        // 分别放到AB两个收银台队列处理器中
            cashProcessorA.customersComing(customerA.toArray(new Customer[customerA.size()]));
            cashProcessorB.customersComing(customerB.toArray(new Customer[customerB.size()]));
        }    /**
         * B顾客排队总是选择 队末尾的那个人商品个数越少的
         */
        private static CashProcessor getCashProcessorB(List<CashProcessor> cashProcessors) {
            CashProcessor cashProcessorB = cashProcessors.get(0);
            for (int i = 1; i < cashProcessors.size(); i++) {
                if (cashProcessors.get(i).getLastCustmGoodsNum() < cashProcessorB
                        .getLastCustmGoodsNum()) {
                    cashProcessorB = cashProcessors.get(i);
                }
            }
            return cashProcessorB;
        }    /**
         * A顾客排队总是选择 人数最少的队
         */
        private static CashProcessor getCashProcessorA(List<CashProcessor> cashProcessors) {
            CashProcessor cashProcessorA = cashProcessors.get(0);
            for (int i = 1; i < cashProcessors.size(); i++) {
                if (cashProcessors.get(i).getCashDeskQueueSize() < cashProcessorA
                        .getCashDeskQueueSize()) {
                    cashProcessorA = cashProcessors.get(i);
                }
            }
            return cashProcessorA;
        }}对于时间的操作,应该可以再来一个抽象,这里就没再做了。
    关于条件1编号的问题,个人感觉应该属于人为设置的,就没再细分了。另外,这面试题,感觉有点大了,一时半会应该搞不定的。或者我想复杂了。
      

  11.   


    真的十分、万分的感谢版主用代码详细的解答。太感谢了。
    我有几个问题
    1. 版主加了一个DeskChoserHelper 类,里面是关于A,和B类型客户如何选择 收银台的,这两个方法能不能写到customer 的类中。
    2. 能否将customer抽象成一个super class, 然后type A 和type B分别是他的一个子类。overridden的方法就是选择 收银台的方式不同。
    3. 最后的main函数中,如何实例这N个cashprocessor, 使其多线程同步,是用for循环将其一个个instance吗,然后放在数组中。 如果是一个个的实现,那customer选择的时候是不是要访问每一个cashprocessor的状态啊?而且这个cashprocessor的状态得在每一分钟都有一个输出,因为几乎每一分钟都有顾客去进行结账,结账前就要去选择这个cashprocessor的队列。 这一点是我一直搞不清楚的,不知道该怎么实现。还有一个请求,既然版主就差一个time没有写了,恳请版主帮我实现了,好嘛,拜托了。
    感激不尽!!!
      

  12.   

    1. DeskChoserHelper是想实现一批顾客对一批收银台的处理辅助工具,如果允许单个的话,可以将顾客选择收银台操作写到顾客类里
    2. type A、B是枚举,区分用的,也可以用int型,没必要做成继承关系。当然,我是图省事儿就写成内部类了,完全可以提取出来单独定义(做成内部类,也是为了说明,这个枚举是给外部类用的)
    3. cashprocessor是实现Runnable的,可以使用new Thread(cashprocessor).start()的方式启动。另外,main方法可以看做最核心的“中央处理器”,它负责定义顾客、收银台实体,并且负责使用DeskChoserHelper实现顾客加入process的事件触发最后呢,那个time,可以定义一个静态的public static int time=0;然后每次事件触发,都实现time+1的操作,打印的时候打印time即可。嗯,代码也是纠结了一段时间才写好的,LZ最好自己改改测测,感觉重点就是两个:业务抽象和多线程使用。其它time什么的都是浮云。
      

  13.   

    这个问题不是一个实际时间问题,不是真的需要等1min,2min,而是在输入的时候所有customer的到达时间是已知的,是用一种算法,去整个系统处理完所有customer 的总时间。是不是可以不用thread去处理,不去等待一定的时间。
    我的想法的是用 到达时间,离开时间,等待时间去处理,不知道这样的想法可以实现吗?
    谢谢啦~
      

  14.   

    OMG,由于是比较初级,很少写到关于线程的东西,真是无从下手,能否说的详细点啊,请指条路~ 
      

  15.   

    多线程解决这道题是很好,但是考虑到是面试题,假设完成时间是1个小时,那么面试官测试这个程序,岂不是要等1个小时,才能等到结果啊~大家有没有想到静态的方法。输入的是一个文件,文件的内容像
     2 (收银台的数量)
    A 1 5
    B 2 1
    A 3 5
    B 5 3
    A 8 2所以这个顾客数是一定的,收银台数是一定的,只需要调用就能使用。
    能不能根据顾客的属性,去选择哪个收银台,比如 到达时间,处理商品时间,完成时间,这几个变量去判断哪个收银台,以及完成的总时间
    就可以不用线程去实现。大家觉得怎么样呢,给点建议吧。谢啦~