关闭

Java多线程同步设计中使用Mutex

2193人阅读 评论(1) 收藏 举报
分类:
 Mutex是互斥体,广泛地应用在多线程编程中。本文以广为流程的Doug Lea的concurrent工具包的Mutex实现为例,进行一点探讨。在Doug Lea的concurrent工具包中,Mutex实现了Sync接口,该接口是concurrent工具包中所有锁(lock)、门(gate)和条件变量(condition)的公共接口,Sync的实现类主要有:Mutex、Semaphore及其子类、Latch、CountDown、ReentrantLock等。这也体现了面向抽象编程的思想,使我们可以在不改变代码或者改变少量代码的情况下,选择使用Sync的不同实现。下面是Sync接口的定义: 

public interface Sync
{
 public void acquire() throws InterruptedException; 
 //获取许可 
 public boolean attempt(long msecs) throws InterruptedException;
 //尝试获取许可 
 public void release(); 
 //释放许可
}

  通过使用Sync可以替代Java synchronized关键字,并提供更加灵活的同步控制。当然,并不是说 concurrent工具包是和Java synchronized独立的技术,其实concurrent工具包也是在synchronized的基础上搭建的,从下面对Mutex源码的解析即可以看到这一点。synchronized关键字仅在方法内或者代码块内有效,而使用Sync却可以跨越方法甚至通过在对象之间传递,跨越对象进行同步。这是Sync及concurrent工具包比直接使用synchronized更加强大的地方。

  注意Sync中的acquire()和attempt()都会抛出InterruptedException,所以使用Sync及其子类时,调用这些方法一定要捕获InterruptedException。而release()方法并不会抛出InterruptedException,这是因为在acquire()和attempt()方法中可能会调用wait()等待其它线程释放锁。而release()在实现上进行了简化,直接释放锁,不管是否真的持有。所以,你可以对一个并没有acquire()的线程调用release()这也不会有什么问题。而由于release()不会抛出InterruptedException,所以我们可以在catch或finally子句中调用release()以保证获得的锁能够被正确释放。比如:

class X 
{
 Sync gate; // ...
 public void m() 
 {
  try
  {
   gate.acquire();
   // block until condition holds
   try 
   {
    // ... method body
   }
   finally { gate.release(); }
  }
  catch (InterruptedException ex) { // ... evasive action } 
 }
}

  Mutex是一个非重入的互斥锁。Mutex广泛地用在需要跨越方法的before/after类型的同步环境中。下面是Doug Lea的concurrent工具包中的Mutex的实现。

public class Mutex implements Sync
{
 /** The lock status **/
 protected boolean inuse_ = false;
 public void acquire() throws InterruptedException 
 {
  if (Thread.interrupted()) throw new InterruptedException();//(1) 
  synchronized(this)
  { 
   try 
   {
    while (inuse_) wait();
    inuse_ = true;
   }
   catch (InterruptedException ex) 
   {
    //(2) 
    notify();
    throw ex;
   }
  }
 }

 public synchronized void release() 
 {
  inuse_ = false; 
  notify();
 }

 public boolean attempt(long msecs) throws InterruptedException 
 {
  if (Thread.interrupted()) throw new InterruptedException();
  synchronized(this) 
  {
   if (!inuse_) 
   {
    inuse_ = true;
    return true;
   }
   else if (msecs <= 0)
    return false;
   else
   {
    long waitTime = msecs; 
    long start = System.currentTimeMillis();
    try
    {
     for (;;) 
     {
      wait(waitTime); 
      if (!inuse_)
      {
       inuse_ = true;
       return true;
      }
      else
      {
       waitTime = msecs - (System.currentTimeMillis() - start);
       if (waitTime <= 0) // (3) 
        return false;
       }
     }
    }
    catch (InterruptedException ex)
    {
     notify();
     throw ex; 
    }
   }
  }
 }
}

  为什么要在acquire()和attempt(0方法的开始都要检查当前线程的中断标志呢?这是为了在当前线程已经被打断时,可以立即返回,而不会仍然在锁标志上等待。调用一个线程的interrupt()方法根据当前线程所处的状态,可能产生两种不同的结果:当线程在运行过程中被打断,则设置当前线程的中断标志为true;如果当前线程阻塞于wait()、sleep()、join(),则当前线程的中断标志被清空,同时抛出InterruptedException。所以在上面代码的位置(2)也捕获了InterruptedException,然后再次抛出InterruptedException。

  release()方法简单地重置inuse_标志,并通知其它线程。

  attempt()方法是利用Java的Object.wait(long)进行计时的,由于Object.wait(long)不是一个精确的时钟,所以attempt(long)方法也是一个粗略的计时。注意代码中位置(3),在超时时返回。
Mutex是Sync的一个基本实现,除了实现了Sync接口中的方法外,并没有添加新的方法。所以,Mutex的使用和Sync的完全一样。在concurrent包的API中Doug给出了一个精细锁定的List的实现示例,我们这儿也给出,作为对Mutex和Sync使用的一个例子:

class Node
{
 Object item; Node next;
 Mutex lock = new Mutex();
 // 每一个节点都持有一个锁 
 Node(Object x, Node n) 
 {
  item = x;
  next = n; 
 }
}

class List

 protected Node head; 
 // 指向列表的头 
 // 使用Java的synchronized保护head域 
 // (我们当然可以使用Mutex,但是这儿似乎没有这样做的必要 
 
 protected synchronized Node getHead() 
 { return head; }
 boolean search(Object x) throws InterruptedException 
 {
  Node p = getHead();
  if (p == null) return false; 
  // (这儿可以更加紧凑,但是为了演示的清楚,各种情况都分别进行处理) 
  p.lock.acquire(); 
  // Prime loop by acquiring first lock.
  // (If the acquire fails due to
  // interrupt, the method will throw
  // InterruptedException now, 
  // so there is no need for any 
  // further cleanup.) 
  for (;;) 
  {
   if (x.equals(p.item)) 
   {
    p.lock.release(); 
    // 释放当前节点的锁 
    return true;
   }
   else
   {
    Node nextp = p.next; 
    if (nextp == null)
    {
     p.lock.release();
     // 释放最后持有的锁 
     return false;
    }
    else
    {
     try
     {
      nextp.lock.acquire(); 
      // 在释放当前锁之前获取下一个节点的锁 
     }
     catch (InterruptedException ex)
     {
      p.lock.release();
      // 如果获取失败,也释放当前的锁 throw ex; 
     }
     p.lock.release();
     // 释放上个节点的锁,现在已经持有新的锁了 
     p = nextp;
    }
   }
  }
 }
 synchronized void add(Object x)
 {
  // 使用synchronized保护head域 
  head = new Node(x, head);
 }
 // ... other similar traversal and update methods ... 
}
0
0
查看评论

关于用信号量Semaphore实现互斥锁Mutex

    在Doug lea的那本著名的《Java并发编程—设计原则与模式》,英文名"Concurrent Programming in Java™: Design Principles and Patterns, Second Edition",书中...
  • javayuan
  • javayuan
  • 2006-08-31 22:08
  • 4008

java线程:互斥锁与读写锁

两种互斥锁机制: 1、synchronized 2、ReentrantLock ReentrantLock是jdk5的新特性,采用ReentrantLock可以完全替代替换synchronized传统的锁机制,而且采用ReentrantLock的方式更加面向对象,也更加灵活,网上有...
  • liyantianmin
  • liyantianmin
  • 2015-01-18 02:38
  • 2372

java中的互斥锁,信号量和多线程等待机制

互斥锁和信号量都是操作系统中为并发编程设计基本概念,互斥锁和信号量的概念上的不同在于,对于同一个资源,互斥锁只有0和1 的概念,而信号量不止于此。也就是说,信号量可以使资源同时被多个线程访问,而互斥锁同时只能被一个线程访问 互斥锁在java中的实现就是 ReetranLock , 在访问一个同步...
  • feifeiwendao
  • feifeiwendao
  • 2016-08-15 17:21
  • 3881

mutex用法和例子

//http://msdn.microsoft.com/zhcn/vcsharp/system.threading.mutex_members.aspxMonitor通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问代码块(通常称为临界区)的能力。当一个线程拥有对象的锁时,其他任何线程都...
  • qianjunxian
  • qianjunxian
  • 2009-08-22 00:06
  • 14362

Java并发:互斥锁和读写锁

互斥锁线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁,当某个线程请求一个由其他线程持有的锁时,发出请求的线程就会阻塞。互斥锁其实提供了一种原子操作,让所有线程以串行的方式执行同步代码块。可重入性:某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功(重复获取),这...
  • jiq408694711
  • jiq408694711
  • 2016-03-29 22:59
  • 2195

Java进程间的同步与互斥实例(实现读者写者问题)

题目描述: 给定一个队列A[1-10][1-100000]、元素编号1-10,其中每个元素包含10万个随机数。创建若干个线程,各循环100次;其中10,100个为读线程,10,100个为更新线程。 1.    读线程每次产生随机数三元组:(i, j, k),其中i:[...
  • qq_24369113
  • qq_24369113
  • 2016-12-25 13:33
  • 2355

深入理解JVM——轻量级锁和偏向锁

轻量级锁JDK1.6出现的。并不能代替重量级锁,本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。HotSpot虚拟机对象头对象自身的运行时数据如:哈希吗(HashCode)、GC分代年龄(Generational GC Age)等,这部分数据的长度在32位和64位...
  • nalanmingdian
  • nalanmingdian
  • 2017-08-23 23:19
  • 159

深入JVM锁机制2-Lock

JVM Lock, ReentrantLock,CLH,CAS,自旋锁,偏向锁
  • chen77716
  • chen77716
  • 2011-07-28 18:15
  • 24702

Java锁--Lock实现原理(底层实现)

关于java lock的底层实现原理,讲的有点深,转载学习!Lock完全用Java写成,在java这个层面是无关JVM实现的。在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock、ReadWriteLock(实现类ReentrantRe...
  • Luxia_24
  • Luxia_24
  • 2016-09-01 20:11
  • 6307

Mutex的lock(), unlock(), tryLock()函数介绍

lock函数和tryLock函数都是用于锁定对象,但他们之间有一定的区别: lock函数是阻塞的,因为它调用WaitForSingleObject函数时传递的第二个参数是INFINITE,表示无限等待下去,所以是阻塞的。 tryLock函数时非阻塞的,调用后立即返回。因为它调用WaitForSi...
  • ameyume
  • ameyume
  • 2012-05-15 11:06
  • 20389
    个人资料
    • 访问:9372570次
    • 积分:78554
    • 等级:
    • 排名:第21名
    • 原创:264篇
    • 转载:2871篇
    • 译文:3篇
    • 评论:800条
    文章分类
    最新评论