转载

JAVA仿真之银行出纳员

学习例子是参照《thinking in java》中修改的,先贴上运行结果:

JAVA仿真之银行出纳员

注意看红框之中的内容,这个仿真要达到这样一个目的:

1.客户队列(无优先级):每隔300MILLS生产一个客户

2.正在服务的出纳员队列(有优先级):队列头始终是服务人数最多的那个出纳员,因为在队列调整的时候需要将服务人数最多的出纳员调整出去做其他的事情(相当于找个轻松的事情做,放松下)

3.做其他事情的出纳员列表(有优先级):队列头始终是服务人数最少的那个出纳员,因为在队列调整的时候需要讲服务人数最少的出纳员调整去服务(为客户办理业务)

队列调整时间为每1秒调整一次

下面贴上代码:

  1 package com.xt.thinks21_8;   2    3 import java.lang.reflect.InvocationTargetException;   4 import java.lang.reflect.Method;   5 import java.util.Comparator;   6 import java.util.LinkedList;   7 import java.util.PriorityQueue;   8 import java.util.Queue;   9 import java.util.Random;  10 import java.util.concurrent.ArrayBlockingQueue;  11 import java.util.concurrent.ExecutorService;  12 import java.util.concurrent.Executors;  13 import java.util.concurrent.TimeUnit;  14   15 /**  16  * 消费者(客户)  17  *   18  * @author Administrator  19  *   20  */  21 class Customer {  22     private final int serviceTime;// 服务时间(客户的业务办理时间)  23   24     public Customer(int tm) {  25         serviceTime = tm;  26     }  27   28     public int getServiceTime() {  29         return serviceTime;  30     }  31   32     public String toString() {  33         return " ● ";  34     }  35 }  36   37 /**  38  * 消费者队列(客户排成一条队伍)  39  *   40  * @author Administrator  41  *   42  */  43 class CustomerLine extends ArrayBlockingQueue<Customer> {  44     /**  45      *   46      */  47     private static final long serialVersionUID = 1L;  48   49     /**  50      * 构造方法传入客户最大人数  51      *   52      * @param maxLineSize  53      */  54     public CustomerLine(int maxLineSize) {  55         super(maxLineSize);  56     }  57   58     /**  59      * 返回所有客户(服务时间)  60      */  61     public String toString() {  62         if (this.size() == 0)  63             return "[Empty]";  64   65         StringBuilder result = new StringBuilder();  66         for (Customer customer : this) {  67             result.append(customer);  68         }  69         return result.toString();  70     }  71 }  72   73 /**  74  * 消费者产生器  75  *   76  * @author Administrator  77  *   78  */  79 class CustomerGenerator implements Runnable {  80     private CustomerLine customers;  81     private static Random rand = new Random(47);  82   83     public CustomerGenerator(CustomerLine cq) {  84         customers = cq;  85     }  86   87     public void run() {  88         try {  89             while (!Thread.interrupted()) {  90                 // 每隔300MILLS就假如一个客户(来办理业务等)  91                 TimeUnit.MILLISECONDS.sleep(rand.nextInt(300));  92                 // 将客户加入到消费者队列  93                 customers.put(new Customer(rand.nextInt(1000)));  94                 // System.out.println(customers.toString());  95             }  96         } catch (InterruptedException e) {  97             System.out.println("CustomerGenerator interrupted");  98         }  99         System.out.println("CustomerGenerator terminating"); 100     } 101 } 102  103 /** 104  * 出纳员,业务员 105  *  106  * @author Administrator 107  *  108  */ 109 class Teller implements Runnable, Comparable<Teller> { 110     private static int counter = 0; 111     private final int id = counter++;// 业务员的id 112     private int customersServed = 0;// 已经服务的客户 113     private CustomerLine customers;// 客户队列 114     private boolean servingCustomerLine = true;// 是否正在服务客户 115     private int count = 0; 116     private boolean sort = true;// 排序方式,true为降序,false升序 117  118     public Teller(CustomerLine cq) { 119         customers = cq; 120     } 121  122     @Override 123     public void run() { 124         try { 125             while (!Thread.interrupted()) { 126                 // 下面两行代表正在为客户服务 127                 Customer customer = customers.take(); 128                 count++; 129                 TimeUnit.MILLISECONDS.sleep(customer.getServiceTime()); 130                 synchronized (this) {// 这里同步的原因是因为禁止执行到这里时线程调用doSomethingElse(),serveCustomerLine(),以及compareTo(Tellero) 131                     customersServed++;// 服务的客户+1 132                     while (!servingCustomerLine) 133                         // 当出纳员去干其他的事情的时候servingCustomerLine会为false 134                         wait();// 代表出纳员正在做其他事情 135                 } 136             } 137         } catch (InterruptedException e) { 138             System.out.println(this + " interrupted"); 139         } 140         System.out.println(this + " terminating");// 结束 141     } 142  143     /** 144      * 出纳员做其他的事情 145      */ 146     public synchronized void doSomethingElse() { 147         // customersServed = 0;//服务过的客户置0 148         servingCustomerLine = false; 149     } 150  151     /** 152      * 出纳员服务客户队列 153      */ 154     public synchronized void serveCustomerLine() { 155         assert !servingCustomerLine : "already serving:" + this; 156         servingCustomerLine = true;// 标识修改 157         notifyAll();// 唤醒线程 158     } 159  160     public String toString() { 161         return "Teller " + id + ":" + count; 162     } 163  164     public String shortString() { 165         return "T" + id; 166     } 167  168     public int getCount() { 169         return count; 170     } 171  172     public void setCount(int count) { 173         this.count = count; 174     } 175  176     public boolean isSort() { 177         return sort; 178     } 179  180     public void setSort(boolean sort) { 181         this.sort = sort; 182     } 183  184     /** 185      * 优先级队列的优先级比较方法 186      */ 187     @Override 188     public synchronized int compareTo(Teller o) { 189         // TODO Auto-generated method stub 190         if (sort)// 降序(目的为了加入到workingTellers队列中) 191             return o.customersServed - customersServed > 0 ? 1 192                     : o.customersServed - customersServed < 0 ? -1 : 0; 193         else 194             // 升序(目的为了加入到tellersDoingOtherThings队列中) 195             return customersServed - o.customersServed > 0 ? 1 196                     : customersServed - o.customersServed < 0 ? -1 : 0; 197     } 198  199 } 200  201 /** 202  * 出纳员管理器 203  *  204  * @author Administrator 205  *  206  */ 207 class TellerManager implements Runnable { 208     private ExecutorService exec;// 执行器 209     private CustomerLine customers;// 客户队列 210     private PriorityQueue<Teller> workingTellers = new PriorityQueue<Teller>();// 降序队列 211     private PriorityQueue<Teller> tellersDoingOtherThings = new PriorityQueue<Teller>();// 升序队列 212     private int adjustmentPeriod;// 调整周期 213  214     public TellerManager(ExecutorService e, CustomerLine customers, 215             int adjustmentPeriod) { 216         exec = e; 217         this.customers = customers; 218         this.adjustmentPeriod = adjustmentPeriod; 219         // 构造出纳员管理器的时候默认新增一个出纳员 220         Teller teller = new Teller(customers);// 默认为降序 221         exec.execute(teller); 222         workingTellers.add(teller); 223     } 224  225     /** 226      * 调整出纳员数量 227      * */ 228     public void adjustTellerNumber() { 229         try { 230             // 利用反射调用PriorityQueue的heapify(刷新队列头)方法 231             Method method = workingTellers.getClass().getDeclaredMethod( 232                     "heapify"); 233             method.setAccessible(true); 234             try { 235                 method.invoke(workingTellers); 236             } catch (IllegalAccessException e) { 237                 // TODO Auto-generated catch block 238                 e.printStackTrace(); 239             } catch (IllegalArgumentException e) { 240                 // TODO Auto-generated catch block 241                 e.printStackTrace(); 242             } catch (InvocationTargetException e) { 243                 // TODO Auto-generated catch block 244                 e.printStackTrace(); 245             } 246         } catch (NoSuchMethodException e) { 247             // TODO Auto-generated catch block 248             e.printStackTrace(); 249         } catch (SecurityException e) { 250             // TODO Auto-generated catch block 251             e.printStackTrace(); 252         } 253  254         // 如果客户队列的size大于出纳员队列的size的两倍,新增出纳员 255         if (customers.size() / workingTellers.size() > 2) { 256             if (tellersDoingOtherThings.size() > 0) { 257                 Teller teller = tellersDoingOtherThings.remove(); 258                 teller.setSort(true);// 设置为降序 259                 teller.serveCustomerLine(); 260                 workingTellers.add(teller); 261                 return; 262             } 263             Teller teller = new Teller(customers);// 默认为降序 264             exec.execute(teller); 265             workingTellers.add(teller); 266             return; 267         } 268         // 如果出纳员的size大于1并且客户队列的size小雨出纳员size的两倍,调用一个出纳员去做其他的事情,从出纳员队列中移除 269         if (workingTellers.size() > 1 270                 && customers.size() / workingTellers.size() < 2) 271             reassignOneTeller(); 272         // 如果客户队列size==0并且出纳员的size大于1,同样移除一个出纳员去做其他的事情 273         if (customers.size() == 0) 274             while (workingTellers.size() > 1) 275                 reassignOneTeller(); 276     } 277  278     /** 279      * 移除一个出纳员去做其他事情 280      * */ 281     private void reassignOneTeller() { 282         Teller teller = workingTellers.poll(); 283         teller.setSort(false); 284         teller.doSomethingElse(); 285         tellersDoingOtherThings.add(teller); 286     } 287  288     public void run() { 289         try { 290             while (!Thread.interrupted()) { 291                 adjustTellerNumber(); 292                 TimeUnit.MILLISECONDS.sleep(adjustmentPeriod); 293                 // 打印客户和出纳员队列 294                 System.out.print("customers[" + customers 295                         + "]    workingTellers["); 296                 for (Teller teller : workingTellers) { 297                     System.out.print(teller.shortString() + ":" 298                             + teller.getCount() + "  "); 299                 } 300                 // 打印在做其他事情的出纳员队列 301                 System.out.print("]    doingOtherThingTellers["); 302                 for (Teller teller : tellersDoingOtherThings) { 303                     System.out.print(teller.shortString() + ":" 304                             + teller.getCount() + "  "); 305                 } 306                 if (tellersDoingOtherThings.size() == 0) 307                     System.out.print("Empty"); 308                 System.out.println("]/n"); 309             } 310         } catch (InterruptedException e) { 311             System.out.println(this + " interrupted"); 312         } 313         System.out.println(this + " terminating"); 314     } 315  316     public String toString() { 317         return "TellerManager"; 318     } 319 } 320  321 public class BankTellerSimulation { 322     static final int MAX_LINE_SIZE = 50; 323     static final int ADJUSTMENT_PERIOD = 1000; 324  325     public static void main(String[] args) throws Exception { 326         ExecutorService exec = Executors.newCachedThreadPool(); 327         CustomerLine customers = new CustomerLine(MAX_LINE_SIZE); 328         exec.execute(new CustomerGenerator(customers)); 329         exec.execute(new TellerManager(exec, customers, ADJUSTMENT_PERIOD)); 330         if (args.length > 0) 331             TimeUnit.SECONDS.sleep(new Integer(args[0])); 332         else { 333             System.out.println("Press 'Enter' to quit"); 334             System.in.read(); 335         } 336         exec.shutdownNow(); 337     } 338 }

代码中注释唯一需要注意的地方是:

 1 try {  2             // 利用反射调用PriorityQueue的heapify(刷新队列头)方法  3             Method method = workingTellers.getClass().getDeclaredMethod(  4                     "heapify");  5             method.setAccessible(true);  6             try {  7                 method.invoke(workingTellers);  8             } catch (IllegalAccessException e) {  9                 // TODO Auto-generated catch block 10                 e.printStackTrace(); 11             } catch (IllegalArgumentException e) { 12                 // TODO Auto-generated catch block 13                 e.printStackTrace(); 14             } catch (InvocationTargetException e) { 15                 // TODO Auto-generated catch block 16                 e.printStackTrace(); 17             } 18         } catch (NoSuchMethodException e) { 19             // TODO Auto-generated catch block 20             e.printStackTrace(); 21         } catch (SecurityException e) { 22             // TODO Auto-generated catch block 23             e.printStackTrace(); 24         }

这个地方可以看出PriorityQueue的设计还是可以完善的,如果PriorityQueue中的队列元素是线程,线程内部需要调整比较因子,那么PriorityQueue没有提供出一个可以刷新队列头的方法的,如果在此demo中不刷新队列头而部分线程(出纳员)服务人数增长过快超过当前队列头的服务人数,则正在服务的队列的排序会造成假象。而heapify这个方法正是刷新队列头的方法可以却没有公开出来,无奈只有通过反射来调用刷新队列头,此处也可以看出reflect机制在JAVA中的强大。

如有错误还请提出指正 JAVA仿真之银行出纳员 JAVA仿真之银行出纳员

正文到此结束
Loading...