ImageVerifierCode 换一换
格式:DOCX , 页数:22 ,大小:28.59KB ,
资源ID:9140050      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/9140050.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Java 并发核心编程.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Java 并发核心编程.docx

1、Java 并发核心编程1、关于java并发2、概念3、保护共享数据4、并发集合类5线程6、线程协作及其他1、关于java并发自从java创建以来就已经支持并发的理念,如线程和锁。这篇指南主要是为帮助java多线程开发人员理解并发的核心概念以及如何应用这些理念。本文的主题是关于具有java语言风格的Thread、synchronized、volatile,以及J2SE5中新增的概念,如锁(Lock)、原子性(Atomics)、并发集合类、线程协作摘要、Executors。开发者通过这些基础的接口可以构建高并发、线程安全的java应用程序。2、概念本部分描述的java并发概念在这篇DZone Re

2、fard会被通篇使用。从JVM并发看CPU内存指令重排序(Memory Reordering):java内存模型详解: 概念描述Java Memory Model Java内存模型在JavaSE5(JSR133)中定义的Java Memory Model(JMM)是为了确保当编写并发代码的时候能够提供Java程序员一个可用的JVM实现。术语JMM的作用类似与一个观察同步读写字段的monitor。它按照“happens-before order(先行发生排序)”的顺序可以解释为什么一个线程可以获得其他线程的结果,这组成了一个属性同步的程序,使字段具有不变性,以及其他属性。monitor Moni

3、torJava语言中,每个对象都拥有一个访问代码关键部分并防止其他对象访问这段代码的“monitor”(每个对象都拥有一个对代码关键部分提供访问互斥功能的“monitor”)。这段关键部分是使用synchronized对方法或者代码标注实现的。同一时间在同一个monitor中,只允许一个线程运行代码的任意关键部分。当一个线程试图获取代码的关键部分时,如果这段代码的monitor被其他线程拥有,那么这个线程会无限期的等待这个monitor直到它被其他线程释放。除了访问互斥之外,monitor还可以通过wait和notify来实现协作。原子字段赋值 Atomic field assignment除

4、了doubles和longs之外的类型,给一个这些类型的字段赋值是一个原子操作。在JVM中,doubles和longs的更新是被实现为2个独立的操作,因此理论上可能会有其他的线程得到一个部分更新的结果。为了保护共享的doubles和longs,可以使用volatile标记这个字段或者在synchronized修饰的代码块中操作字段。竞争状态 Race condition竞争发生在当不少于一个线程对一个共享的资源进行一系列的操作,如果这些线程的操作的顺序不同,会导致多种可能的结果。数据竞争 Data race数据竞争主要发生在多个线程访问一个共享的、non-final、non-volatile、

5、没有合适的synchronization限制的字段。Java内存模型不会对这种非同步的数据访问提供任何的保证。在不同的架构和机器中数据竞争会导致不可预测的行为。安全发布 Safe publications在一个对象创建完成之前就发布它的引用时非常危险的。避免这种使用这种引用的一种方法就是在创建期间注册一个回调接口。另外一种不安全的情况就是在构造子中启动一个线程。在这2种情况中,非完全创建的对象对于其他线程来说都是可见的。不可变字段 Final Fields不可变字段在对象创建之后必须明确设定一个值,否则编译器就会报出一个错误。一旦设定值后,不可变字段的值就不可以再次改变。将一个对象的引用设定为

6、不可变字段并不能阻止这个对象的改变。例如,ArrayList类型的不可变字段不能改变为其他ArrayList实例的引用,但是可以在这个list实例中添加或者删除对象。 在创建结尾,对象会遇到”final field freeze”:如果对象被安全的发布后,即使在没有synchronization关键字修饰的情况下,也能保证所有的线程获取final字段在构建过程中设定的值。final field freezer不仅对final字段有用,而且作用于final对象中的可访问属性。不可变对象 Immutable objects在语法上final 字段能够创建不需要synchronization修饰的、

7、能够被共享读取的线程安全的不可变对象。实现Immutable Object需要保证如下条件: 对象被安全的发布(在创建过程中this 引用是无法避免的)所有字段被声明为final在创建之后,在对象字段能够被访问的范围中是不允许修改这个字段的。class被声明为final(为了防止subclass违反这些规则)3、保护共享数据编写线程安全的java程序,当修改共享数据的时候要求开发人员使用合适的锁来保护数据。锁能够建立符合Java Memory Model要求的访问顺序,而且确保其他线程知道数据的变化。注意:在Java Memory Model中,如果没有被synchronization修饰,改

8、变数据不需要什么特别的语法表示。JVM能够自由地重置指令顺序的特性和对可见性的限制方式很容易让开发人员感到奇怪。3.1、Synchronized每个对象实例都拥有一个每次只能让一个线程锁住的monitor。synchronized能够用在一个方法或者代码块中来锁住这个monitor。用synchronized修饰一个对象,当修改这个对象的一个字段,synchronized保证其他线程余下的对这个对象的读操作能够获取修改后的值。需要注意的是修改同步块之外的数据或者synchronized没有修饰当前被修改的对象,那么不能保证其他线程读到这些最新的数据。synchronized关键字能够修饰一个对

9、象实例中的函数或者代码块。在一个非静态方法中this关键字表示当前的实例对象。在一个synchronized修饰的静态的方法中,这个方法所在的类使用Class作为实例对象。3.2、LockJava.util.concurrent.locks包中有个标准Lock接口。ReentrantLock 实现了Lock接口,它完全拥有synchronized的特性,同时还提供了新的功能:获取Lock的状态、非阻塞获取锁的方法tryLock()、可中断Lock。下面是使用ReentrantLock的详细示例:public class Counter private final Lock lock = new

10、 ReentrantLock();private int value;public int increment() lock.lock();try return +value;finallylock.unlock();3.3、ReadWriteLockJava.util.concurrent.locks包中还有个ReadWriteLock接口(实现类是ReentrantWriteReadLock),它定义一对锁:读锁和写锁,特征是能够被并发的读取但每次只能有一个写操作。使用ReentrantReadWriteLock并发读取特性的详细示例:public class ReadWrite priv

11、ate final ReadWriteLock lock = new ReentrantReadWriteLock();private int value;public void increment()lock.writeLock().lock();tryvalue+;finallylock.writeLock().unlock();public int current()lock.readLock().lock();tryreturn value;finallylock.readLock().unlock();3.4、volatilevolatile原理与技巧: volatile修饰符用来标

12、注一个字段,表明任何对这个字段的修改都必须能被其他随后访问的线程获取到,这个修饰符和同步无关。因此,volatile修饰的数据的可见性和synchronization类似,但是这个它只作用于对字段的读或写操作。在JavaSE5之前,因为JVM的架构和实现的原因,不同JVM的volatile效果是不同的而且也是不可信的。下面是Java内存模型明确地定义volatile的行为:public class Processor implements Runnable private volatile boolean stop;public void stopProcessing()stop = true

13、;public void run() while (!stop) /do processing注意:使用volatile修饰一个数组并不能让这个数组的每个元素拥有volatile特性,这种声明只是让这个数组的reference具有volatile属性。数组被声明为AtomicIntegerArray类型,则能够拥有类似volatile的特性。3.5、原子类使用volatile的一个缺点是它能够保证数据的可见性,却不能在一个原子操作中对volatile修饰的字段同时进行校验和更新操作。java.util.concurrent.atomic包中有一系列支持在单个非锁定(lock)的变量上进行原子操

14、作的类,类似于volatile。示例:public class Counter private AtomicInteger value = new AtomicInteger();private int value;public int increment() return value.incrementAndGet();incrementAndGet方法是原子类的复合操作的一个示例。booleans, integers, longs, object references, integers数组, longs数组, object references数组 都有相应的原子类。3.6、Thread

15、Local通过ThreadLocal能数据保存在一个线程中,而且不需要lock同步。理论上ThreadLocal可以让一个变量在每个线程都有一个副本。ThreadLocal常用来屏蔽线程的私有变量,例如“并发事务”或者其他的资源。而且,它还被用来维护每个线程的计数器,统计,或者ID生成器。public class TransactionManager private static final ThreadLocal currentTransaction= new ThreadLocal() Overrideprotected Transaction initialValue() return

16、new NullTransaction();public Transaction currentTransaction() Transaction current = currentTransaction.get();if(current.isNull() current = new TransactionImpl();currentTransaction.put(current);return current;4、Concurrent Collections(并发集合类)保护共享数据的一个关键技术是在存储数据的类中封装同步机制。所有对数据的使用都要经过同步机制的确认使这个技术能够避免数据的不

17、当访问。在java.util.concurrent包中有很多为并发使用情况下设计的数据结构。通常,使用这些数据结构比使用同步包装器装饰的非同步的集合的效率更高。4.1、Concurrent lists and sets在Table2 中列出了java.util.concurrent包中拥有的3个并发的List和Set实现类。类描述CopyOnWriteArraySetCopyOnWriteArraySet在语意上提供写时复制(copy-on-werite)的特性,对这个集合的每次修改都需要对当前数据结构新建一个副本,因此写操作发费很大。在迭代器创建的时候,会对当前数据数据结构创建一个快照用于迭

18、代。CopyOnWriteArrayListCopyOnWriteArrayList和CopyOnWriteArraySet类似,也是基于copy-on-write语义实现了List接口ConcurrentSkipListSetConcurrentSkipListSet(在JavaSE 6新增的)提供的功能类似于TreeSet,能够并发的访问有序的set。因为ConcurrentSkipListSet是基于“跳跃列表(skip list)”实现的,只要多个线程没有同时修改集合的同一个部分,那么在正常读、写集合的操作中不会出现竞争现象。skip list: http:/zh.wikipedia.

19、org/zh-cn/%E8%B7%B3%E8%B7%83%E5%88%97%E8%A1%A84.2、Concurrent mapsJava.util.concurrent包中有个继承Map接口的ConcurrentMap的接口,ConcurrentMap提供了一些新的方法(表3)。所有的这些方法在一个原子操作中各自提供了一套操作步骤。如果将每套步骤在放在map之外单独实现,在非原子操作的多线程访问的情况下会导致资源竞争。表3:ConcurrentMap的方法:方法描述putIfAbsent(K key, V value) : V如果key在map中不存在,则把key-value键值对放入map

20、中,否则不执行任何操作。返回值为原来的value,如果key不存在map中则返回nullremove(Object key, Object value) : boolean如果map中有这个key及相应的value,那么移除这对数据,否则不执行任何操作replace (K key, V value) : V如果map中有这个key,那么用新的value替换原来的value,否则不执行任何操作replace (K key, V oldValue, V newValue) : boolean如果map中有这对key-oldValue数据,那么用newValue替换原来的oldValue,否则不执行

21、任何操作在表4中列出的是ConcurrentMap的2个实现类方法描述ConcurrentHashMapConcurrentHashMap提供了2种级别的内部哈希方法。第一种级别是选择一个内部的Segment,第二种是在选定的Segment中将数据哈希到buckets中。第一种方法通过并行地在不同的Segment上进行读写操作来实现并发。(ConcurrentHashMap是引入了Segment,每个Segment又是一个hash表,ConcurrentHashMap相当于是两级Hash表,然后锁是在Segment一级进行的,提高了并发性。 )ConcurrentSkipListMapConc

22、urrentSkipListMap(JavaSE 6新增的类)功能类似TreeMap,是能够被并发访问的排序map。尽管能够被多线程正常的读写只要这些线程没有同时修改map的同一个部分,ConcurrentSkipListMap的性能指标和TreeMap差不多。4.3、QueuesQueues类似于沟通“生产者”和“消费者”的管道。组件从管道的一端放入,然后从另一端取出:“先进先出”(FIFO)的顺序。Queue接口在JavaSE5新添加到java.util中的,能够被用于单线程访问的场景中,主要适用于多个生产者、一个或多个消费者的情景,所有的读写操作都是基于同一个队列。java.util.c

23、oncurrent包中的BlockingQueue接口是Queue的子接口,而且还添加了新的特性处理如下场景:队列满(此时刚好有一个生产者要加入一个新的组件)、队列空(此时刚好有一个消费者读取或者删除一个组件)。BlockingQueue提供如下方案解决这些情况:一直阻塞等待直到其他线程修改队列的数据状态;阻塞一段时间之后返回,如果在这段时间内有其他线程修改队列数据,那么也会返回。表5:Queue和BlockingQueue的方法:方法策略插入移除核查Queue抛出异常addremoveelement返回特定的值offerpollpeekBlocking Queue一直阻塞puttaken/a

24、超时阻塞offerpolln/a在JDK中提供了一些Queue的实现,在表6中是这些实现类的关系列表。方法描述PriorityQueuePriorityQueue是唯一一个非线程安全的队列实现类,用于单线程存放数据并且将数据排序。CurrentLinkedQueue一个无界的、基于链接列表的、唯一一个线程安全的队列实现类,不支持BlockingQueue。ArrayBlockingQueue一个有界的、基于数组的阻塞队列。LinkedBlockingQueue一个有界的、基于链接列表的阻塞队列。有可能是最常用的队列实现。PriorityBlockingQueue一个无界的、基于堆的阻塞队列。队

25、列根据设置的Comparator(比较器)来确定组件读取、移除的顺序(不是队列默认的FIFO顺序)DelayQueue一个无界的、延迟元素(每个延迟元素都会有相应的延迟时间值)的阻塞队列实现。只有在延时期过了之后,元素才能被移除,而且最先被移除的是延时最先到期的元素。SynchronousQueue一种0容量的队列实现,生产者添加元素之后必须等待消费者移除后才可以返回,反之依然。如果生产者和消费者2个线程同时访问,那么参数直接从生产者传递到消费者。经常用于线程之间的数据传输。4.4、Deque在JavaSE6中新增加了两端都可以添加和删除的队列-Deque (发音”deck”,not ”dic

26、k”). Deques不仅可以从一端添加元素,从另一端移除,而且两端都可以添加和删除元素。如同BlockingQueue,BlockingDeque接口也为阻塞等待和超时等待的特殊情况提供了解决方法。因为Deque继承Queue、BlockingDeque继承BlockingQueue,下表中的方法都是可以使用的:接口头或尾策略插入移除核查QueueHead抛出异常addFirstremoveFirstgetFirst返回特定的值offerFirstpollFirstpeekFirstTail抛出异常addLastremoveLastgetLast返回特定的值offerLastpollLast

27、peekLastBlockingQueueHead一直阻塞putFirsttakeFirstn/a超时阻塞offerFirstpollFirstn/aTail一直阻塞putLasttakeLastn/a超时阻塞offerLastpollLastn/aDeque的一个特殊应用场景是只在一个端口进行添加、删除、检查操作堆栈(first-in-last-out顺序)。Deque接口提供了stack相同的方法:push(), pop()和peek(),这方法和addFirst(), removeFirst(), peekFirst()一一对应,可以把Deque的任何一个实现类当做堆栈使用。表6中是JD

28、K中Deque和BlockingDeque的实现。注意Deque继承Queue,BlockingDeque继承自BlockingQueue。表8:Deques5、线程在Java中,java.lang.Thread类是用来代表一个应用或者JVM线程。代码是在某个线程类的上下文环境中执行的(使用Thread.currentThread()来获取当前运行的线程)。5.1、线程通讯线程之间最简单的通讯方式是一个线程直接调用另一个线程对象的方法。表9中列出的是线程之间可以直接交互的方法。表9:线程协作方法类描述LinkedList这个经常被用到的类在JavaSE6中有了新的改进-实现了Deque接口。在

29、LinkedList中,可以使用标准的Deque方法来添加或者删除list两端的元素。LinkedList还可以被当做一个非同步的堆栈,用来替代同步的Stack类ArrayDeque一个非同步的、支持无限队列长度(根据需要动态扩展队列的长度)的Deque实现类LinkedBlockingDequeLinkeBlockingDeque是Deque实现中唯一支持并发的、基于链接列表、队列长度可选的类。线程方法描述start启动一个线程实例,并且执行它的run() 方法。join一直阻塞直到其他线程退出interrupt中断其他线程。线程如果在一个方法中被阻塞,会对interrupt操作做出回应,并在这个方法执行的线程中抛出InterruptedException异常;否则线程的中断状态被设定。stop, suspend, resume, destroy这些方法都被废弃,不应该再使用了。因为线程处理过程中状态问题会导致危险的操作。相反,应该使用interrupt() 或者 volatile标示来告诉一个线程应该做什么。5.2、”未捕获异常”处理器线程能够指定一个UncaughtExceptionHandler来接收任何一个导致线程非正常突然终止的未捕获异常的通知。5.3、死锁当存在多个线程(最少2个)等待对方占有的资源,就会形成资源循环依赖和线程等待,产生死锁。最常见的

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1