`

JAVA.util.concurrent 同步框架(翻译三)

阅读更多

 

接上一篇: 

http://caoyaojun1988-163-com.iteye.com/blog/1290759

 

4、运用:

AbstractQueuedSynchronizer类将上述功能联系在一起,作为一个“模板方法模式[6]”中的模板类,作为其他同步器的基类。子类只是实现预定义方法,实现通过获取锁和释放锁的操作来检查和更新状态。然而,AbstractQueuedSynchronizer的子类本身不可用于ADTS,因为这些类暴露的用于内部控制获取和释放的策略的方法,应该对用户不可见。所有java.util.concurrent包中的同步类声明了一个私有的内部AbstractQueuedSynchronizer子类用于委托它的所有同步方法。这也使得public方法可以根据不太的同步器给予适当的名称。

 

例如,假设最小的Mutex类,当同步状态为0意味着解锁,为1意味着锁定。这个类的同步方法不需要参数值,因此直接默认使用零。

 

 

class Mutex {
	class Sync extends AbstractQueuedSynchronizer {
		public boolean tryAcquire(int ignore) {
			return compareAndSetState(0, 1);
		}
		public boolean tryRelease(int ignore) {
			setState(0); 
			return true;
		}
	}
	private final Sync sync = new Sync();
	public void lock() { sync.acquire(0); }
	public void unlock() { sync.release(0); }
}

 

 

此示例的更全面的版本,以及与其他使用文档,可以在J2SE文档中找到,当然还有许多变种。例如,tryAcquire可以使用“test-and-test-and-set“改变值之前检查状态值。

 

这可能是令人惊讶的,使用委托(delegation )和虚方法(virtual methods),来构造性能敏感的互斥锁, 然而,各种面向对象的设计,动态编译器早已非常成熟;当同步器频繁调用的是。他们都可以很好的优化掉这个开销;AbstractQueuedSynchronizer类还提供了一些方法,可以设置同步类的控制策略。例如,基于acquire方法的超时和中断的版本、独占模式的同步器、锁。AbstractQueuedSynchronizer类也提供了一系列的方法(如acquireShared),他们与tryAcquireShared和tryReleaseShared方法不同,可以通知框架(通过其返回值)将来的acquires方法是否可以成功;最终可以实现级联信号唤醒多个线程。

 

尽管序列化(永久存储或传输)同步器通常是不明智的,但是由于这些类通常用于构建其他类,如线程安全的集合。所以通常实现序列化。AbstractQueuedSynchronizer和ConditionObject类提供的方法来序列化同步状态,而不是被阻塞线程的底层或其他本质上临时状态。即便如此,大多数的同步类反序列化时只是复位同步状态作为初始值,与内置锁反序列化时总是设置为解锁状态的隐含的政策相同。虽然不是必须的,但仍明确支持final域的反序列化。

 

4.1 公平性

 

虽然他们是基于FIFO队列,同步器不一定是公平的。可以注意到,在基本的acquire算法中(3.3节),tryAcquire是再排队前进行检查。因此,新请求获取锁(acquiring)的线程可以优先于队列的头部第一个节点对应的线程。

 

这虽然破坏了FIFO的策略,但是也有普遍高于其他技术的总吞吐量。这减少了有锁可以用,但是因为预定的下一个线程还在唤醒(unblocking)的过程中,所以还没有线程获取到锁的时间,同时,它通过只允许一个(队列的第一个)线程唤醒,避免了过度的、无用的、竞争;可以实现自己的tryAcquire方法,在交回控制权前简单的多试几次,这样可以加剧不公平性, 如果有需要,开发者可以自己创建一个自己的简单持有的同步器;

 

FIFO同步器是相对最公平的锁;即便它会被打破,一个等待唤醒的(unpark)的线程与所有打破规则进入的线程都有一个公平的竞争机会,如果失败它会重新阻塞;当然,如果闯入的线程比获取一个等待唤醒的线程唤醒到达的快,队列中第一个线程几乎没有赢的概率,所以几乎总是reblock, 简单持有的同步器,通常用于多个闯入线程和多个等待唤醒的线程在多处理器的场景,此时队列中的第一个线程被唤醒;如下图所示,既要维持一个或多个线程时处理器的利用率,同时也要避免饥饿。


 

如果需要更加公平的策略,也相对比较简单。程序员可以自己定义 tryAcquire,如果不是队列的head节点就失败(返回false),达到严格的公平;可以通过少数提供的检查方法之一getFirstQueuedThread,来检查是否是第一个节点。

 

一个更快,更严格的变种是如果队列(暂时)是空,也让tryAcquire成功,在这种情况下,如果多个线程遇到一个空队列的情况下竞争,其中至少有一个及第一个得到执行权的线程不需要入队。这一策略使得在所有java.util.concurrent中的同步器,支持“公平”的模式。

 

虽然在实践中往往希望公平,但是即便设置公平也不会得到保证,因为Java语言规范并不提供调度保证。例如,即使有一个严格意义上的公平同步,如果他们从来不需要阻止等待对方,JVM可以纯粹按照顺序选择运行线程,而实际上,在单处理器的环境中,这些线程在被上下文切换之前可以每次运行一个时间片。如果有一个线程持有一个互斥锁,它必须暂时先得到时间片才能释放锁,不然就阻塞那些需要锁的线程,这导致延长了同步器是可用的,但是不能被线程获取到的时间;使用公平的同步器往往在多处理器有很好的表现,因为这样有更好的并发,因此出现竞争的几率更多。

 

即使他们在高竞争的场景下,性能不是很理想,但是公平锁任然可以工作的很好,同时也保持编码的简洁。例如:当维护相对较长的代码或者延长锁间的时间间隔,在这种情况下,可以提示一定的性能,但是饥饿的风险更大。同步框架将最终的决定权交由用户。

 

4.2 同步器

 

这里描述一个草图关于怎样使用这个框架定义java.util.concurrent中的同步器;ReentrantLock类使用同步状态(递归)维护锁的数量。在获取锁的时候,它会记录当前线程的ID,并且递归检查是否有异常线程获取锁导致的非法的状态异常。该类也提供ConditionObject,暴露它的监测和检查方法。同时支持“公平”的模式的选项,在内部通过实现两个不同AbstractQueuedSynchronizer的子类(公平的实现不允许打破规则),并为每个ReentrantLock的实例选择以个合适的实现。

 

ReentrantReadWriteLock类使用16位的同步状态持有写锁定计数,其余16位持有读锁计数。WriteLock使用与ReentrantLock同样的结构。ReadLock使用acquireShared的方法,以允许多个读者。

 

Semaphore类(一个计数信号)使用同步状态保持当前计数。它定义acquireShared方法递减计数,如果为负数了就阻塞当前线程;同样使用tryRelease递增计数,如果是正数的时候,就唤醒线程。

 

CountDownLatch类使用同步状态代表计数。当减到零的时候所有所有获取锁的操作全部成功返回。

 

FutureTask类使用同步状态来代表future 的运行状态(初始,运行,注销,完成)。调用释放(release)来设置或取消一个future ,等待线程处理完成,acquire返回唤醒线程。

 

SynchronousQueue类(一个CSP风格的切换)使用内部等待节点调整生产者和消费者;它采用了同步状态来作为标识,当消费者改变状态的时候允许生产者继续处理,反之亦然。

 

java.util.concurrent包的用户当然可以为自己的应用程序定义自己的同步器;例如,在那些经常遇到,但是包里没有提供的各种Win32事件的语义类,二进制闩锁,锁集中管理,并基于树的壁垒。

 

原文见第一篇的附件

下一篇:http://caoyaojun1988-163-com.iteye.com/blog/1315089

 

 

本站支持 pay for your wishes

  • 大小: 12.2 KB
0
0
分享到:
评论

相关推荐

    JAVA_API1.6文档(中文)

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 ...

    juconcurrent:java.util.concurrent

    Java JUC的使用1.volatile关键字-内存可见性2.原子变量-CAS算法3.ConcurrentHashMap锁分段机制4.CountDownLatch闭锁5.实现Callable接口6.Lock同步锁7.Condition控制线程通信8.线程按序交替9.ReadWriteLock读写锁10....

    Java基础知识点总结.docx

    无论是工作学习,不断的总结是必不可少的。只有不断的总结,发现问题,弥补不足,才能长久的...java.util.concurrent.locks包下常用的类 326 NIO(New IO) 327 volatile详解 337 Java 8新特性 347 Java 性能优化 362

    java jdk-api-1.6 中文 chmd

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 ...

    JavaAPI中文chm文档 part2

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 ...

    Java 1.6 API 中文 New

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 java...

    java 多线程同步

    不客气地说,创建 java.util.concurrent 的目的就是要实现 Collection 框架对数据结构所执行的并发操作。通过提供一组可靠的、高性能并发构建块,开发人员可以提高并发类的线程安全、可伸缩性、性能、可读性和可靠性...

    java api最新7.0

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 java...

    [Java参考文档].JDK_API 1.6

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 java...

    JavaAPI1.6中文chm文档 part1

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 ...

    [Java参考文档]

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 ...

    J.U.C-AQS框架同步组件之闭锁CountDownLatch介绍

    CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它们都存在于java.util.concurrent包下。CountDownLatch这个类能够使一个线程...

    Java中LockSupport的使用.docx

    LockSupport是JDK1.6中在java.util.concurrent中的子包locks中引入的一个比较底层的工具类,用来创建锁和其他同步工具类的基本线程阻塞原语。java锁和同步器框架的核心 AQS: AbstractQueuedSynchronizer,就是通过...

    JDK_1_6 API

    java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 ...

    Java 面试宝典

    Java 基础部分..................................................................................................................... 7 1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么...

Global site tag (gtag.js) - Google Analytics