学习例子是参照《thinking in 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中的强大。
如有错误还请提出指正。