Java资源网

| JAVA基础 | 环境配置 | JDBC | 线程技术 | Socket编程 | JavaMail | JAVA与XML | 设计模式 | 技术新闻 | Java认证 | 程序人生 软件下载
| JSP&Servlet | Spring | Struts | Hibernate | JBuilder | Eclipse | WebService | EJB技术 | J2ME开发 | 应用服务器 | JXTA | Ajax
Articles search文章搜索
   关键字:
   类 别:
       
New download 最新下载
· [组件]HTML Parser 1.5
· [教程]WebSphere Studio应用教程
· [组件]JDom 1.0
· [工具]Junit3.8.1
· [教程]EJB编程及J2EE系统架构和设计
· [教程]EJB教程
· [教程]J2EE Tutorial中文版
· [教程]Java编程思想2(英文)
· [教程]java编程思想(完整版)
· [教程]Java网络编程
New articles 最新文章
· 设计移动 Web 服务
· 解析XML的时候完全忽略DTD
· 理解XML Schema XML Schema 初步
· 标签库的深入研究
· 提升JSP应用程序的七大绝招
· 如何使用JDOM对XML文件进行操作
· 处理XML字符串中特殊字符
· 利用Digester把XML转换成为Java对象
· 使用WebService 和RMI远程协作
· 使用Axis开发Web Service程序
Articles top 热门文章
· Eclipse基础--plugin插件安装(6644)
· eclipse+tomcat+lomboz的安装配置说明(4774)
· Java程序员就业前景(4584)
· Windows下JAVA环境变量的设置祥解(3788)
· Tomcat下JSP、Servlet和JavaBean环境的配置(3716)
· 使用links方式安装Eclipse插件(3698)
· 一个老程序员的心理话(3533)
· linux下jdk的安装与配置(3459)
· 初学者入门:Structs中基本配置入门(3334)
· Eclipse 运行命令行参数大全(3084)
您的位置:首页>>线程技术>>关于多线程同步的初步教程--可重入锁的设计及使用
关于多线程同步的初步教程--可重入锁的设计及使用
2007-04-13   来源:www.javaresearch.org  作者:未知

    上一篇中的Mutex是一个独占锁,只能有一个线程拥有该锁,并且即时是同一个线程,如果已经持有一个Mutex时,再次企图获取该锁时仍然会阻塞。有的时候我们需要锁能够像Java语言的synchronized那样,同一个线程可以重新进入,只要已经拥有了该锁,而不用在该锁上阻塞。我们可以对上篇中的Mutex的实现进行改造,实现一个可重入的锁--ReentrantLock。这需要ReentrantLock中记录当前锁的拥有者(线程),同时设置一个整型变量,记录当前线程进入的次数。

  1. public class ReentrantLock implements Sync  {
  2.   protected Thread owner_ = null;
  3.   protected long holds_ = 0;
  4.   //......
  5. }

在获取、释放锁时,首先判断该线程是否是锁的拥有者。如果是当前线程已经拥有该锁,则在每一次acquire()时增1,在release()时减1在次数减少到0时,说明该锁的当前拥有者已经完全释放该锁,不再拥有该锁。所以,将拥有者设置为null。如果当前线程不是锁的拥有者,那么在企图获取锁时在该锁上wait(),在release()方法中,如果拥有者已经完全释放锁,那么就将拥有者清零,并notify()其它线程。

  1.   public void acquire() throws InterruptedException {
  2.     if (Thread.interrupted()) throw new InterruptedException();
  3.     Thread caller = Thread.currentThread();
  4.     synchronized(this) { // 在this上同步
  5.       if (caller == owner_) 
  6.         ++holds_;
  7.       else {
  8.         try {  
  9.           while (owner_ != null) wait(); 
  10.           owner_ = caller;
  11.           holds_ = 1;
  12.         }
  13.         catch (InterruptedException ex) {
  14.           notify();
  15.           throw ex;
  16.         }
  17.       }
  18.     }
  19.   }
  20.   public synchronized void release()  { //在this上同步
  21.     if (Thread.currentThread() != owner_)
  22.       throw new Error("Illegal Lock usage"); 
  23.     if (--holds_ == 0) {
  24.       owner_ = null;
  25.       notify(); 
  26.     }
  27.   }


注意上面的代码要对owner_和holds_在this上进行同步,以解决在这两个变量上的竞态条件。attempt()方法实现和Mutex类似,也添加了锁拥有者的检查及计数:

  1.   public boolean attempt(long msecs) throws InterruptedException {
  2.     if (Thread.interrupted()) throw new InterruptedException();
  3.     Thread caller = Thread.currentThread();
  4.     synchronized(this) {
  5.       if (caller == owner_) {
  6.         ++holds_;
  7.         return true;
  8.       }
  9.       else if (owner_ == null) {
  10.         owner_ = caller;
  11.         holds_ = 1;
  12.         return true;
  13.       }
  14.       else if (msecs <= 0)
  15.         return false;
  16.       else {
  17.         long waitTime = msecs;
  18.         long start = System.currentTimeMillis();
  19.         try {
  20.           for (;;) {
  21.             wait(waitTime); 
  22.             if (caller == owner_) {
  23.               ++holds_;
  24.               return true;
  25.             }
  26.             else if (owner_ == null) {
  27.               owner_ = caller;
  28.               holds_ = 1;
  29.               return true;
  30.             }
  31.             else {
  32.               waitTime = msecs - (System.currentTimeMillis() - start);
  33.               if (waitTime <= 0) 
  34.                 return false;
  35.             }
  36.           }
  37.         }
  38.         catch (InterruptedException ex) {
  39.           notify();
  40.           throw ex;
  41.         }
  42.       }
  43.     }
  44.   }


由于ReentrantLock增加了对拥有者的计数,所以,也提供了额外的两个方法:holds()和release(long),用于返回当前线程进入的次数(如果当前线程不拥有该锁,则holds()返回0),以及一次性释放多个锁:

  1.   public synchronized long holds() {
  2.     if (Thread.currentThread() != owner_) return 0;
  3.     return holds_;
  4.   }
  5.   public synchronized void release(long n) {
  6.     if (Thread.currentThread() != owner_ || n > holds_)
  7.       throw new Error("Illegal Lock usage"); 
  8.     holds_ -= n;
  9.     if (holds_ == 0) {
  10.       owner_ = null;
  11.       notify(); 
  12.     }
  13.   }


使用实例


Doug Lea的concurrent包现在已经给广泛使用,在JBoss的org.jboss.mx.loading.UnifiedLoaderRepository 中就使用了concurrent包中的ReentrantLock进行JBoss中的类装载中的同步控制。下面看org.jboss.mx.loading.UnifiedLoaderRepository中对ReentrantLock的使用:

  1. public class UnifiedLoaderRepository
  2.         extends LoaderRepository
  3.         implements NotificationBroadcaster, UnifiedLoaderRepositoryMBean
  4.   {
  5.        private ReentrantLock reentrantLock = new ReentrantLock(); //生成一个重入锁
  6.        public Class loadClass(String name, boolean resolve, ClassLoader cl) 
  7. throws ClassNotFoundException
  8.         {
  9.            try
  10.            {
  11.               try
  12.               {
  13.                  // Only one thread at a time can load classes
  14.                  // Pass the classloader to release its lock when blocking the thread
  15.                  // We cannot use synchronized (this), as we MUST release the lock
  16.                  // on the classloader. Change this only after discussion on the
  17.                  // developer's list !
  18.                  synchronize(cl); //对传入的ClassLoader上进行同步
  19.      
  20.                  // This syncronized block is necessary to synchronize with add/removeClassLoader
  21.                  // See comments in add/removeClassLoader; we iterate on the classloaders, must avoid
  22.                  // someone removes or adds a classloader in the meanwhile.
  23.                  synchronized (this)
  24.                  {
  25.                     // Try the cache before anything else.
  26.                     Class cls = loadClassFromCache(name, cl);
  27.      
  28.                     // Found in cache, we're done
  29.                     if (cls != null) {return cls;}
  30.      
  31.                     // Not found in cache, ask the calling classloader
  32.                     cls = loadClassFromClassLoader(name, resolve, cl);
  33.      
  34.                     // The calling classloader sees the class, we're done
  35.                     if (cls != null) {return cls;}
  36.      
  37.                     // Not visible by the calling classloader, iterate on the other classloaders
  38.                     cls = loadClassFromRepository(name, resolve, cl);
  39.      
  40.                     // Some other classloader sees the class, we're done
  41.                     if (cls != null) {return cls;}
  42.      
  43.                     // This class is not visible
  44.                     throw new ClassNotFoundException(name);
  45.                  }
  46.               }
  47.               finally
  48.               {
  49.                  unsynchronize(cl); //使用完毕后释放重入锁
  50.               }
  51.            }
  52.            catch (ClassCircularityError x)
  53.            {
  54.          //........
  55.          }     
  56.      }
  57.      //.........
  58. }


上面代码中的synchronize()和unsynchronize()方法如下:

  1.      private void synchronize(ClassLoader cl)
  2.       {
  3.          // This method
  4.          // 1- must allow only one thread at a time,
  5.          // 2- must allow a re-entrant thread,
  6.          // 3- must unlock the given classloader waiting on it,
  7.          // 4- must not hold any other lock.
  8.          // If these 4 are not done, deadlock will happen.
  9.          // Point 3 is necessary to fix Jung's RFE#4670071
  10.          // Beware also that is possible that a classloader arrives here already locked
  11.          // (for example via loadClassInternal()) and here we cannot synchronize on 'this'
  12.          // otherwise we deadlock in loadClass() where we first synchronize on 'this' and
  13.          // then on the classloader (resource ordering problem).
  14.          // We solve this by using a reentrant lock.
  15.          // Save and clear the interrupted state of the incoming thread
  16.          boolean threadWasInterrupted = Thread.currentThread().interrupted();
  17.          try
  18.          {
  19.             // Only one thread can pass this barrier
  20.             // Other will accumulate here and let passed one at a time to wait on the classloader,
  21.             // like a dropping sink
  22.             reentrantLock.acquire();
  23.             while (!isThreadAllowed(Thread.currentThread()))
  24.             {
  25.                // This thread is not allowed to run (another one is already running)
  26.                // so I release() to let another thread to enter (will come here again)
  27.                // and they will wait on the classloader to release its lock.
  28.                // It is important that the wait below is not wait(0) since it may be
  29.                // possible that a notifyAll arrives before the wait.
  30.                // It is also important that this release() is outside the sync block on
  31.                // the classloader, to avoid deadlock with threads that triggered
  32.                // loadClassInternal(), locking the classloader
  33.                reentrantLock.release();
  34.                synchronized (cl)
  35.                {
  36.                   // Threads will wait here on the classloader object.
  37.                   // Waiting on the classloader is fundamental to workaround Jung's RFE#4670071
  38.                   // However, we cannot wait(0), since it is possible that 2 threads will try to load
  39.                   // classes with different classloaders, so one will enter, the other wait, but
  40.                   // since they're using different classloaders, nobody will wake up the waiting one.
  41.                   // So we wait for some time and then try again.
  42.                   try {cl.wait(137);}
  43.                   catch (InterruptedException ignored) {}
  44.                }
  45.                // A notifyAll() has been issued, all threads will accumulate here
  46.                // and only one at a time will pass, exactly equal to the barrier
  47.                // before the 'while' statement (dropping sink).
  48.                // Must be outside the synchronized block on the classloader to avoid that
  49.                // waiting on the reentrant lock will hold the lock on the classloader
  50.                try
  51.                {
  52.                   reentrantLock.acquire();
  53.                }
  54.                catch (InterruptedException ignored)
  55.                {
  56.                }
  57.             }
  58.          }
  59.          catch(InterruptedException ignored)
  60.          {
  61.          }
  62.          finally
  63.          {
  64.             // I must keep track of the threads that entered, also of the reentrant ones,
  65.             // see unsynchronize()
  66.             increaseThreadsCount();
  67.             // I release the lock, allowing another thread to enter.
  68.             // This new thread will not be allowed and will wait() on the classloader object,
  69.             // releasing its lock.
  70.             reentrantLock.release();
  71.   
  72.             // Restore the interrupted state of the thread
  73.             if( threadWasInterrupted )
  74.                Thread.currentThread().interrupt(); 
  75.          }
  76.       }
  77.  
  78.       private void unsynchronize(ClassLoader cl)
  79.       {
  80.          // Save and clear the interrupted state of the incoming thread
  81.          boolean threadWasInterrupted = Thread.currentThread().interrupted();
  82.          try
  83.          {
  84.             reentrantLock.acquire();
  85.    
  86.             // Reset the current thread only if we're not reentrant
  87.             if (decreaseThreadsCount() == 0)
  88.             {
  89.                setCurrentThread(null);
  90.             }
  91.          }
  92.          catch (InterruptedException ignored)
  93.          {
  94.          }
  95.          finally
  96.          {
  97.             reentrantLock.release();
  98.    
  99.             // Notify all threads waiting on this classloader
  100.             // This notification must be after the reentrantLock's release() to avoid this scenario:
  101.             // - Thread A is loading a class in the ULR
  102.             // - Thread B triggers a loadClassInternal which locks the UCL
  103.             // - Thread A calls unsynchronize, locks the reentrantLock 
  104.             //    and waits to acquire the lock on the UCL
  105.             // - Thread B calls synchronize and waits to lock the reentrantLock
  106.             synchronized (cl)
  107.             {
  108.                cl.notifyAll();
  109.             }
  110.    
  111.             // Restore the interrupted state of the thread
  112.             if( threadWasInterrupted )
  113.                Thread.currentThread().interrupt(); 
  114.          }
  115.       }




  --相关文章--
· 彻底明白Java的多线程-线程间的通信 (2007-04-13)
· 彻底明白Java的多线程-实现多线程及线程的同步 (2007-04-13)
· 对Swing线程的再思索nbsp;(下) (2007-04-13)
· 如何使用线程 (2007-04-13)
· 关于多线程同步的初步教程--可重入锁的设计及使用 (2007-04-13)
· 关于多线程同步的初步教程--使用synchronized (2007-04-13)

版权所有©2005-2006 JAVA资源网 渝ICP备05007591号 虚拟主机 | 关于我们 | 联系方式 | 广告业务 | 网站地图 | 友情链接