分享

java并发(3):并发工具集

 碧海山城 2010-10-27

如果多个变量间的变化不是彼此独立的,某个值的改变会制约其他几个变量,因此,更新一个变量的时候,要在同一个原子操作中更新其他几个。为了保护状态的一致性,要在单一的原子操作中更新相互关联的状态变量

1.1 Synchronized

Java提供了强制原子性的内置锁机制,Synchronized块:一个块有两部分,锁对象的引用,以及这个锁保护的代码块。同一时间,只有一个线程可以运行特定锁保护的代码块,因此由同一个锁保护的synchronized块会各自原子地执行,不会相互干扰-----确保一组语句(statements)作为单独的,不可分割的单元。

可重进入

一个线程请求其他线程已经占有的锁时,请求线程被阻塞。然后内部锁,对于同一个线程的代码,内部锁具有可重进入,因此线程在试图获得它自己占有的锁时,请求会成功。重进入方便了锁行为的封装,简化了开发。假设有AB两个类,A继承B,分别有ab方法,都加了synchronized关键字,那么在a调用b的时候,,都会试图获得锁,如果没有重进入,那么有一个锁就用于无法得到,一直处于等待状态。

大家都知道可以用锁来解决并发问题,但在具体使用上还有很多讲究,比如:

· 每个共享的可变变量都需要由一个个确定的锁保护。

· 一旦使用了锁,就意味着这段代码的执行就丧失了操作系统多道程序的特性,会在一定程度上影响性能

· 锁不能解决在分布式环境共享变量的并发问题

1.2 Lock

JDK1.5以后的Lock是锁的一个抽象,它允许把锁定的实现作为Java类,而不是语言的特性来实现。就为Lock的多种实现留下了空间,各种实现可以有不同的调度算法,性能特性或者锁定语义。

Synchronized确保了共享变量的原子性和可见性,它能够实现同步,但是,它也有一些限制:无法中断一个正在等候获得锁的线程,也无法通过投票得到锁

1.3 ReentrantLock,可重入锁

ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,但是他添加了类似锁投票,定时锁等候和可中断锁等候的一些特性。不过它在使用的时候必须在finallyunLock,另外JVMsynchronized管理锁定请求和释放时,JVM在生成线程转存储时能够包括锁定信息,这样对调试很有用。Lock只是普通的类,JVM不知道具体哪个线程拥有Lock对象。

所以在大多数情况下,使用synchronizeed更好,除非确实需要synchronized没有的特性,比如时间锁等候,可中断锁等候、无块结构锁、和锁投票等

ReentrantLock构造器的一个参数是boolean值,它允许选择一个公平(fair)锁,还是一个不公平锁。公平锁使线程按照请求顺序依次获得锁,不公平锁并不是按顺序来的。CPU的线程调度本来就是不公平的,JVM保证了所有线程都会得到他们所等候的锁,大多数情况下,确保所得公平性的成本非常高,比synchronized高的多,所以默认情况下,使用false的参数就可以了

1.4 ReadWriteLock

维护了一对相关的锁,一个用于只读操作,另一个用于写入操作,只要没有writer,读取锁可以由多个reader线程同时保持。写入锁是独占地。

与互斥锁相比,读-写锁允许对共享数据进行更高级别的访问。虽然依次只有一个线程可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据。

例如,某个数据填充后,不经常对其进行修改,因为不经常对其进行修改,所以这种情况,用读-写锁笔记哦合适。

并发容器

 

原子变量类与CAS

java中确保共享变量线程安全的传统方式是使用同步,同步可以确定访问一组变量的所有线程都将拥有对这些变量的独占访问权(原子性),并且其他线程获得该锁定时,将可以看到对这些变量的更改(可见性)。但是锁的代价太昂贵,特别是在竞争很厉害的时候,影响吞吐量。

3.1 CAS

支持并发的第一个处理器提供原子的测试并设置操作,通常在单位上运行这项操作。现在的处理器(包括 Intel 和 Sparc 处理器)使用的最通用的方法是实现名为 比较并转换或 CAS 的原语。CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)

CAS 原理:

我认为位置 应该包含值 A;如果包含该值,则将 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

基于 CAS 的并发算法称为 无锁定算法,因为线程不必再等待锁定(有时称为互斥或关键部分,这取决于线程平台的术语)。无论 CAS 操作成功还是失败,在任何一种情况中,它都在可预知的时间内完成。如果 CAS 失败,调用者可以重试 CAS 操作或采取其他适合的操作。

3.2 非阻塞算法

个线程的失败或挂起不应该影响其他线程的失败或挂起.这类算法称之为非阻塞(nonblocking)算法

对比阻塞算法:
如果有一类并发操作其中一个线程优先得到对象监视器的锁当其他线程到达同步边界时就会被阻塞.直到前一 个线程释放掉锁后才可以继续竞争对象锁.(当然,这里的竞争也可是公平的按先来后到的次序)

3.3 原子类

JDK 5.0之后,在 java.util.concurrent.atomic 包中添加原子变量类之后,这种情况才发生了改变。所有原子变量类都公开比较并设置原语(与比较并交换类似),这些原语都是使用平台上可用的最快本机结构(比较并交换、加载链接/条件存储,最坏的情况下是旋转锁)来实现的。 java.util.concurrent.atomic 包中提供了原子变量的 种风格。 

3.4 ABA问题

假设第一次读取V地址的A然后通过CAS来判断V地址的值是否仍旧为A, 如果是就将B的值写入V地址,覆盖A.

但是语义上有一个漏洞当第一次读取VA此时内存V的值变为B然后在未执行CAS又变回了A.
此时, CAS再执行时会判断其正确的并进行赋值.

这种判断值的方式来断定内存是否被修改过针对某些问题是不适用的.为了解决这种问题, jdk 1.5并发包提供了AtomicStampedReference(有标记的原子引用)通过控制变量值的版本来保证CAS正确性.

其实大部分通过值的变化来CAS, 已经够用了.

Synchronizer 同步器

线程池

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多