Java 多线程(Thread) 同步(synchronized) 以及 wait, notify 相关 [实例介绍]
场景描述
有一家很大的商场,在某市有几个商品配送中心,并有几家分店,这家商场经营很多的商品,平时运营情况是这样的:
根据各分店的商品销售情况,给分店配送相应需求量的商品;并上架到分店指让的位置,供客户选购。
客户选择自己需要的商品,然后到收银台交钱打包;
然后到一天的某个时候分店管理员(经理等),开始统计当前的销售情况,并计划向商品配送中心订购各商品的配送量;
场景模拟
1. 商场类;
public class StoreMart { //商场这一天剩余的商品量 private static Map<String, Integer> goodsMap = new HashMap<String, Integer>(); private static Map<String, Integer> priceMap = new HashMap<String, Integer>(); static { goodsMap.put("ap", 1000); goodsMap.put("bp", 500); goodsMap.put("cp", 2000); priceMap.put("ap", 20); priceMap.put("bp", 50); priceMap.put("cp", 30); //... } //选择商品 //pn 商品名称 //num 选购数量 public synchronized void selGoods(String name,String pn, int num) { int total = getNum(pn); int remain = total - num; goodsMap.put(pn, remain); //保存用户购物车信息 } //新增商品数据 public synchronized void putGoods(String pn, int num) { int total = getNum(pn); total = total + num; goodsMap.put(pn, total); } public static int getNum(String pn) { int num = 0; if (goodsMap.containsKey(pn)) { num = goodsMap.get(pn); } return num; } //结算 public void settleGoods(Map<String, Integer> goods) { //.... } }
用户购物类:
public class UserShop implements Runnable { private static StoreMart sm = new StoreMart(); private final String name; private final Map<String, Integer> sgoods = new HashMap<String, Integer>(); public UserShop(String name) { this.name = name; } public void add(String pn, int num) { sgoods.put(pn, num); } public void run() { for(Map.Entry<String, Integer> entry:sgoods.entrySet()){ sm.selGoods(entry.getKey(), entry.getValue()); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } sm.settleGoods(sgoods); System.out.println(this.name + " 购物完毕! "); } public static void main(String[] args) { UserShop u1 = new UserShop("NameA"); u1.add("ap",1); u1.add("bp",2); Thread t1 = new Thread(u1); UserShop u2 = new UserShop("NameB"); u1.add("ap",4); u1.add("bp",5); Thread t2 = new Thread(u2); t1.start(); t2.start(); } }
以上摸拟的是商品数量都充足的情况;
摸拟了两个客户 各购买了 不同数量的商品 ap, bp;
但是如果万一用户多了起来,如果有几个客户一下子要选购同一个商品很多的量;就会出问题了;
使用 wait(), notify()
修改 下 StoreMart 类:
//选择商品 //pn 商品名称 //num 选购数量 public synchronized void selGoods(String name,String pn, int num) { int total = getNum(pn); while (total < num) { System.out.println(pn + "商品量不够"); this.wait(); } int remain = total - num; goodsMap.put(pn, total); //保存用户购物车信息 } //新增商品数据 public synchronized void putGoods(String pn, int num) { int total = getNum(pn); total = total + num; goodsMap.put(pn, total); this.notify(); }
在UserShop 的 run 方法里,添加商品不足补货的语句:
public void run() { for(Map.Entry<String, Integer> entry:sgoods.entrySet()){ sm.selGoods(entry.getKey(), entry.getValue()); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //发现商品不够了 sm.putGoods("ap", 100); sm.putGoods("bp", 200); System.out.println(this.name + " 购物完毕! "); }
这样 当商场里的 某商品量不足时,NameA 或 NameB 就得处于等待状态,就是要wait() 到等待集合,等待被 notify() 换醒;
synchronized, wait, notify说明
1. wait;
1>. 用于 当本 线程不能成立时,放弃当前占用的锁;
2>. wait 必须要在 synchronized 所在的代码块内;
3>. wait 是 Object 的方法;
4>. 一般要处理 while 循环 体内; 用于重新判断条件是否满足;
2. synchronized 一般结束多线程环境下,用于处理 占用 共享资源的问题;
3. notify 用于 唤醒 wait() 的线程;
wait 与 sleep 的区别
wait 针对的是 当前 synchronized 所在块的机锁:
1>. synchronized 如果 标识一个 普通类,那机锁就为当前 所在对象的 锁;
2>. synchronized 如果 标识一个 静态类,那机锁 就为所在类的类锁;
3>. 如果是 synchronized (obj) ,则 锁 为 obj 对象; 一般这个锁可以定义为 0 长度的 byte 数组,比较经济;
sleep 针的是当前 Thread;
sleep 一般是定时执行的;
wait 是需要竞争的,能否执行,得看本次锁是否分配给了它,本且条件是否满足;
interrupt() 都可以中止 sleep 或 wait 的暂停状态;如果线程 A 要中止 线程 B,则调用 线程B实例的 interrupt() 方法,当 线程B处于 wait(),sleep(),join() 时,就会抛出 InterruptedException 异常,在 catch 块执行 return ,即可正常结束线程;