服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

java多线程同步-busyflag或lock

我们首先开发一个busyflag的类,类似于c++中的simaphore。

  1. public class busyflag {
  2.     protected thread busyflag = null;
  3.     protected int busycount = 0;
  4.     
  5.     public synchronized void getbusyflag() {
  6.         while (trygetbusyflag() == false) {
  7.             try {
  8.                 wait();
  9.             } catch (exception e) {}            
  10.         }
  11.     }
  12.     
  13.     private synchronized boolean trygetbusyflag() {
  14.         if (busyflag == null) {
  15.             busyflag = thread.currentthread();
  16.             busycount = 1;
  17.             return true;
  18.         }
  19.         
  20.         if (busyflag == thread.currentthread()) {
  21.             busycount++;
  22.             return true;
  23.         }
  24.         return false;        
  25.     }
  26.     
  27.     public synchronized void freebusyflag() {
  28.         if(getowner()== thread.currentthread()) {
  29.             busycount--;
  30.             if(busycount==0) {
  31.                 busyflag = null;
  32.                                      notify();
  33.                             }
  34.         }
  35.     }
  36.     
  37.     public synchronized thread getowner() {
  38.         return busyflag;
  39.     }
  40. }


注:参考scott oaks & henry wong《java thread》

busyflag有3个公开方法:getbusyflag, freebusyflag, getowner,分别用于获取忙标志、释放忙标志和获取当前占用忙标志的线程。使用这个busyflag也非常地简单,只需要在需要锁定的地方,调用busyflag的getbusyflag(),在对锁定的资源使用完毕时,再调用改busyflag的freebusyflag()即可。下面我们开始改造上篇中的account和atm类,并应用busyflag工具类使得同时只有一个线程能够访问同一个账户的目标得以实现。首先,要改造account类,在account中内置了一个busyflag对象,并通过此标志对象对account进行锁定和解锁:

  1. import java.util.collections;
  2. import java.util.hashmap;
  3. import java.util.map;
  4. class account {
  5.     string name;
  6.     //float amount;
  7.     
  8.     busyflag flag = new busyflag();
  9.     
  10.     //使用一个map模拟持久存储
  11.     static map storage = new hashmap();
  12.     static {
  13.         storage.put("john"new float(1000.0f));
  14.         storage.put("mike"new float(800.0f));
  15.     }
  16.     
  17.     static map accounts = collections.synchronizedmap(new hashmap());    
  18.     
  19.     
  20.     private account(string name) {
  21.         this.name = name;
  22.         //this.amount = ((float)storage.get(name)).floatvalue();
  23.     }
  24.     
  25.     public synchronized static account getaccount (string name) {
  26.         if (accounts.get(name) == null)
  27.             accounts.put(name, new account(name));
  28.         return (account) accounts.get(name);
  29.     }
  30.     public synchronized void deposit(float amt) {
  31.         float amount = ((float)storage.get(name)).floatvalue();
  32.         storage.put(name, new float(amount + amt));
  33.     }
  34.     public synchronized void withdraw(float amt) throws insufficientbalanceexception {
  35.         float amount = ((float)storage.get(name)).floatvalue();
  36.         if (amount >= amt)
  37.             amount -= amt;
  38.         else 
  39.             throw new insufficientbalanceexception();
  40.                 
  41.         storage.put(name, new float(amount));
  42.     }
  43.     public float getbalance() {
  44.         float amount = ((float)storage.get(name)).floatvalue();
  45.         return amount;
  46.     }
  47.     
  48.     public void lock() {
  49.         flag.getbusyflag();
  50.     }
  51.     
  52.     public void unlock() {
  53.         flag.freebusyflag();
  54.     }
  55. }



新的account提供了两个用于锁定的方法:lock()和unlock(),供account对象的客户端在需要时锁定account和解锁account,account通过委托给busyflag来提供这个机制。另外,大家也发现了,新的account中提供了对account对象的缓存,同时去除了public的构造方法,改为使用一个静态工厂方法供用户获取account的实例,这样做也是有必要的,因为我们希望所有的atm机同时只能有一个能够对同一个account进行操作,我们在account上的锁定是对一个特定account对象进行加锁,如果多个atm同时实例化多个同一个user的account对象,那么仍然可以同时操作同一个账户。所以,要使用这种机制就必须保证account对象在系统中的唯一性,所以,这儿使用一个account的缓存,并将account的构造方法变为私有的。你也可以说,通过在account类锁上进行同步,即将account中的busyflag对象声明为static的,但这样就使同时只能有一台atm机进行操作了。这样,在一台atm机在操作时,全市其它的所有的atm机都必须等待。
另外必须注意的一点是:account中的getaccount()方法必须同步,否则,将有可能生成多个account对象,因为可能多个线程同时到达这个方法,并监测到accounts中没有“john”的account实例,从而实例化多个john的account实例。s

atm类只需作少量改动,在login方法中锁定account,在logout方法中解锁:

  1. public class atm {
  2.     account acc;
  3.     
  4.     //作为演示,省略了密码验证
  5.     public synchronized boolean login(string name) {
  6.         if (acc != null)
  7.             throw new illegalargumentexception("already logged in!");
  8.         acc = account.getaccount(name);
  9.         acc.lock();
  10.         return true;
  11.     }
  12.     
  13.     public void deposit(float amt) {
  14.         acc.deposit(amt);
  15.     }
  16.     
  17.     public void withdraw(float amt) throws insufficientbalanceexception  {
  18.             acc.withdraw(amt);
  19.     }
  20.     
  21.     public float getbalance() {
  22.         return acc.getbalance();
  23.     }
  24.     
  25.     public synchronized void logout () {
  26.         acc.unlock();
  27.         acc = null;
  28.     }
  29.     
  30. }



atmtester类不需要做任何修改即可同样运行,同时保证同一个account同时只能由一个atm进行操作。解决了上篇提到的多个atm同时对同一个account进行操作造成的问题。

在最新的doug lea的util.concurrent工具包中(现处于jsr166)提供了类似的并发实用类:reentrantlock,它实现了java .util.concurrent.locks.lock接口(将在jdk1.5中发布),它的作用也类似于我们这儿的busyflag,实现机制、使用方法也相似。但这是一个工业强度的可重入锁的实现类。在reentrantlock的api文档中有它的使用示例:

  1.      lock l = ...; 
  2.      l.lock();
  3.      try {
  4.          // access the resource protected by this lock
  5.      } finally {
  6.          l.unlock();
  7.      }

扫描关注微信公众号