您好,欢迎访问三七文档
2013.08kaqike主要内容1.Java内存模型2.volatile关键字3.Java线程状态4.线程安全5.J2SEAPI概览1.1线程问题的产生让计算机并发执行若干个运算任务;一个服务端同时对多个客户端提供服务更充分地利用计算机处理器的性能产生问题:缓存一致性引入高速缓存,解决速度矛盾处理器和存储速度之间的巨大差距运算任务至少需要内存交互处理器、高速缓存、主内存之间的交互关系缓存一致性:多处理器系统中,因为共享同一主内存,当多个处理器的运算任务都设计到同一块内存区域时,将可能导致各自的缓存数据不一致的情况,则同步回主内存时需要遵循一些协议。乱序执行优化:为了使得处理器内部的运算单位能尽量被充分利用。z=x+y;if(z0)thenp=m+n;elsep=m-n;要求:原子性、可见性、有序性1.2Java虚拟机内存模型目标是定义程序中各个变量的访问规则。(包括实例字段、静态字段和构成数组的元素,不包括局部变量和方法参数)1.所有的变量都存储在主内存中(虚拟机内存的一部分)。2.每条线程都由自己的工作内存,线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。3.线程之间无法直接访问对方的工作内存中的变量,线程间变量的传递均需要通过主内存来完成。严谨VS宽松线程、主内存、工作内存三者之间的关系内存间交互协议1.lock(锁定):作用于主内存的变量它拔一个变量标识为一条线程独占的状态。2.unlock(解锁):作用于主内存的变量,它把一个处于锁定状态变量释放出来,释放后的变量才可以被其他线程锁定。3.read(读取):作用于主内的变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。4.load(载入):作用于工作内存的变量,把read读取操作从主内存中得到的变量值放入工作内存的变量拷贝中。5.use(使用):作用于工作内存的变量,把工作内存中一个变量的值传递给java虚拟机执行引擎,每当虚拟机遇到一个需要使用到变量值的字节码指令时将会执行该操作。6.assign(赋值):作用于工作内存变量,把一个从执行引擎接收到的变量的值赋值给工作变量,每当虚拟机遇到一个给变量赋值的字节码时将会执行该操作。7.store(存储):作用于工作内存的变量,把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。8.write(写入):作用于主内存的变量,把store操作从工作内存中得到的变量值放入主内存的变量中。成员变量使用、赋值过程内存交互规则1.不允许read和load、store和write操作之一单独出现。2.不允许一个线程丢弃最近的assign操作,变量在工作内存中改变了之后必须把该变化同步回主内存中。3.不允许一个线程没有发生过任何assign操作把数据从线程的工作内存同步回主内存中。4.一个新的变量只能在主内存中诞生。5.一个变量在同一时刻只允许一条线程对其进行lock操作,但可以被同一条线程重复执行多次。6.如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行read、load操作。7.如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作。8.对一个变量执行unlock操作前,必须先把该变量同步回主内存中。2.1synchronized关键字2.2volatile关键字Java语言中的volatile变量可以被看作是一种“程度较轻的synchronized”;与synchronized块相比,volatile变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是synchronized的一部分。1.保证此变量对所有线程的可见性。每条线程使用此类型变量前都需要先刷新,执行引擎看不到不一致的情况。load必须与use同时出现;assign和store必须同时出现。2.禁止指令重排序优化。普通的变量仅保证在方法执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证赋值操作的顺序与程序代码中的顺序一致。volatile应用场景有限的一些情形下使用volatile变量替代锁,必须满足1.运算结果并不依赖变量的当前值、或者确保只有单一的线程修改变量的值。2.变量不需要与其他的状态变量共同参与不变约束。①状态标志②一次性安全发布(one-timesafepublication)③独立观察(independentobservation)④“volatilebean”模式⑤开销较低的读-写锁策略参考:Java理论与实践:正确使用Volatile变量volatile应用场景:开销较低的读-写锁策略volatile性能1.主要原因是其简易性2.次要原因是其性能:某些情况下,volatile变量同步机制的性能要优于锁,很难做出准确、全面的评价。volatile读操作开销非常低,几乎和非volatile读操作一样;volatile写操作的开销要比非volatile写操作多很多,因为要保证可见性需要实现内存界定(MemoryFence),即便如此,volatile的总开销仍然要比锁获取低;volatile操作不会像锁一样造成阻塞;因此,在能够安全使用volatile的情况下,volatile可以提供一些优于锁的可伸缩特性。如果读操作的次数要远远超过写操作,与锁相比,volatile变量通常能够减少同步的性能开销。2.3原子性、可见性与有序性1.原子性:基本数据类型的访问读写是具备原子性的,synchronized块之间的操作也具备原子性。2.可见性:指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。synchronized(规则8)和final可以保证可见性。Final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把this的引用传递出去,那么在其他线程中就能看见final字段的值。3.有序性:volatile本身包含了禁止指令重排序的语义,而synchronized则是由规则5获得的,这个规则决定了持有同一个所的两个同步块只能串行地进入。先行发生原则Java内存模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,其实就是说在发生操作B之前,操作A产生的影响能被操作B观察到。1.程序次序规则:在一个线程内,按照代码控制流顺序,在前面的操作先行发生于后面的操作。2.管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。3.volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作。4.线程启动规则:Thread对象的start()方法先行发生于此线程的每个操作。5.线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测。6.线程中断规则:对线程的interrupt()方法的调用先行发生于被中断线程的代码检测中断事件的发生。7.对象终结过则:一个对象的初始化完成先行发生于它的finalize()方法的开始。8.传递性:如果操作A先行发生于操作B,操作B现象发生于操作C,那么就可以得出操作A先行发生于操作C的结论。3Java线程状态新建NEW:运行RUNNABLE:无限期等待WAITING:等得其他线程显式地唤醒。没有设置Timeout参数的Object.wait();没有设置Timeout参数的Thread.wait()。限期等待TIMED_WAITING:在一定时间之后会由系统自动唤醒。设置Timeout参数的Object.wait();设置Timeout参数的Thread.wait();Thread.sleep()方法。阻塞BLOCKED:等待获取一个排它锁,等待进入一个同步区域。结束TERMINATED:Java线程状态转换4.1线程安全定义:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交换执行,也不需要进行额外的同步,或者调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。线程安全级别1.不可变:只要一个不可变的对象被正确地构建出来。使用final关键字修饰的基本数据类型;如果共享数据是一个对象,那就需要保证对象的行为不会对其状态产生任何影响(String类的对象)。方法:把对象中带有状态的变量都申明为final,如Integer类。有:枚举类型、Number的部分子类(AtomicInteger和AtomicLong除外)。2.绝对线程安全:3.相对线程安全:对这个对象单独的操作是线程安全的。一般意义上的线程安全。4.线程兼容:需要通过调用端正确地使用同步手段来保证对象在并发环境中安全地使用。5.线程对立:不管调用端是否采取了同步措施,都无法在多线程环境中并发使用的代码。有:System.setIn()、System.setOut()、System.runFinalizersOnExit()线程安全级别4.2线程安全的实现4.2.1互斥同步互斥同步:同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条线程使用。互斥方式:临界区、互斥量和信号量。Synchronized关键字:编译后会在同步块前后分别形成monitorenter和monitorexit这两个字节码指令。这两个指令都需要一个引用类型的参数来指明要锁定和解锁的对象。如果没有明确指定对象参数,那就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。在执行monitorenter指令时,首先尝试获取对象的锁,如果没有被锁定或者当前线程已经拥有了该对象的锁,则将锁计数器加1,相应的执行moniterexit时,将锁计数器减1,当计数器为0时,锁就被释放了。如果获取对象锁失败,则当前线程就要阻塞等待。4.2.1互斥同步互斥机制存在以下问题:1.在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。2.一个线程持有锁会导致其它所有需要此锁的线程挂起。3.如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。ReentrantLock相对synchronized的高级功能:等待可中断:当持有锁的线程长期不释放锁时,正在等待的线程可以选择放弃等待,改为处理其他事情。公平锁:多个线程在等待同一个锁时,必须按照申请锁的事件顺序来一次获取锁;而非公平锁在被释放时,任何一个等待锁的线程都有机会获得锁。Synchronized中的锁是非公平锁,ReentrantLock默认也是非公平锁。锁绑定多个条件:一个ReentrantLock对象可以同时绑定多个Condition对象。java.util.concurrent.locks.ReentrantLock4.2.2非阻塞同步基于冲突检测的乐观并发策略:先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再进行其他的补偿措施(一般是不断的尝试,直到成功为止)。AtomicInteger等原子类中提供了方法实现了CAS(CompareandSwap)指令。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。4.2.3无同步方案可重入代码:可以在代码执行的任何时刻中断它,转而去执行另一段代码,而在控制权返回后,原来的程序不会出现任何错误。特征:不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数传入,不调用非可重入的方法等。如果一个方法,它的返回结果是可以预测的,只要出入了相同的数据,就能返回相同的结果,那它就满足可重入性的要求。线程本地存储:如果一段代码中所需要的数据必须与其它代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行。ThreadLocal类ThreadLocal:线程级别的局部变量,为每个
本文标题:Java多线程分享
链接地址:https://www.777doc.com/doc-3401970 .html