您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > Java并发编程实践
黄兴海线程进程一个程序运行单元独立的内存空间不同的进程拥有不同的代码和数据空间可以申请和拥有系统资源线程轻量级进程一个进程可以有多个线程共享相同的内存空间程序计数器、堆栈等是不同的创建线程Thread封装了线程的行为。优先级:1~10,默认为5优先级越大,越会优先执行守护线程(daemon)守护线程不会影响JVM的生命周期一般当所有非守护线程退出后,JVM才会退出Thread(ThreadGroupgroup,Runnabletarget,Stringname,longstackSize)group:线程组,将多个线程封装成一个线程组,可以在线程组中查看线程的数目、线程的列表等信息target:待运行的任务name:线程的名称(以便于标识线程)stackSize:线程栈的大小。控制线程具体见java.lang.Thread.State初始(New)已经创建,但是未运行可运行(Runnable)表示正在JVM中执行可能正在等待处理器等资源阻塞(Blocked)被监视锁所阻塞无限等待(Waiting)无限期地等待另一个线程执行特定的操作有限等待(Timed_Waiting)有限时间内等待另一个线程执行特定的操作终结(Terminated)运行结束控制线程wait/notify协作object等待队列线程T线程3线程T进入object的等待队列后来,线程3也进入等待队列notifyAll会唤醒等待队列中的所有线程Notify会不确定地唤醒某一线程线程1wait/notify协作object必须是监视锁的monitor修改condition、wait和notify均必须在监视锁内部object.wait被唤醒后,最好再次检测条件是否满足除非你知道不会出错?synchronized关键字要求代码块必须同步执行,那么notifyAll之后,各个线程的执行次序是如何的呢?object等待队列线程3线程1线程2wait/notify!在notifyAll之后,各个被唤醒的线程必须先获取监视锁调用notifyAll的线程释放监视锁被唤醒的线程竞争监视锁某线程竞争成功后,运行代码,最后释放监视锁余下线程重复竞争,直至所有代码都执行完毕性能?如果有N个唤醒的线程,那么会面临大量线程对监视锁的竞争如果唤醒后又重新wait,下次notifyAll时又会面临同样的问题一个坏案例T1、T2、…、Tn都在等待队列Q进入数据,然后从Q中取出一个数据T0每秒往Q插入一个数据,并notifyT1、T2…、TnT*会竞争成功,然后去除一个数据,于是队列Q又空了,其他线程发现队列已经空了,重新等待直至N秒后,所有线程处理结束。看看唤醒操作有多少次?但是,还是建议尽量使用notifyAll正确是第一位的~WaitingBlockedRunnable被唤醒后,竞争锁竞争锁成功Terminated不符合唤醒条件重新等待符合唤醒条件成功执行完毕joinsomeThread.join(…)等待线程someThread终止someThread.wait会在线程结束时被唤醒中断每个线程都有一个中断状态someThread.Interrupt()设置someThread的中断状态为truesomeThread.isInterrupted()返回someThread的中断状态Thread.interrupted()清除当前进程的中断状态,并返回之前的值某些阻塞方法会监视线程何时被中断,然后抛出相应的异常Object.wait,Thread.join,Thread.sleep1.清除中断状态2.抛出InterruptedException异常InterruptibleChannel的I/O操作上受阻1.关闭通道2.设置中断状态3.抛出ClosedByInterruptExceptionSelector中受阻1.立刻从选择操作中返回(可能是非零值)2.设置中断状态除非了解线程的中断策略,否则是不应该中断此线程的中断中断的意义传递请求中断的消息不意味着必须停止线程正在执行的工作可以使用Thread.stop()来立刻停止线程,但是这是有风险的处理中断方式一:传递中断异常方式二:设置中断状态方式三:忽略中断一些建议不应该随意忽略中断状态,除非是符合期望的通用的任务或库中,建议传递中断异常如果无法传递异常(如Runnable类型),可以设置中断状态使用中断来取消任务风险安全性正确地发布对象正确地同步对象正确地使用对象活跃度死锁活锁弱响应饥饿安全性风险无论何时,只要有多于一个的线程同时访问给定的状态变量,而且其中有线程修改此状态变量,则必须通过同步来协调线程对改变量的访问。同步机制synchronized关键字volatile变量显式锁原子变量为何要同步可见性在可共享内存的多处理器体系架构中,每个处理器都有自己的缓存,并且周期性的与主内存保证一致有可能,不同的处理器在相同的存储位置可能看到不同的值重排序JVM允许指令的重排序,只需要其保证满足JVM的存储模型原子性比如:多个线程来操作计数变量线程封闭既然共享对象很烦人,各种不容易,那么最好的选择就是不共享线程封闭技术把对象封闭在一个线程之中方式伪线程限制栈限制ThreadLocal样例绝大多数GUI程序要求所有对可视化组件和数据模型对象的修改都限制在事件分发线程中,否则会抛出异常Swing、Android、…JDBC连接池每次从连接池中取出Connection对象,使用完毕之后归还到池内Connection对象本身是不安全的,但是将之封闭在单个线程之后,使用它就没有问题了伪线程限制和栈限制伪线程限制指要求具体的实现去维护线程限制的任务。无法严格限制具体的实现很容易出错伪线程限制的一个样例对于某个volatile变量,如果要求实现中,只有一个线程来修改这个变量,则是线程安全的。在栈限制中,只能通过本地变量才可以触及对象Java保证了基本类型的本地变量总是线程封闭的对于引用类型的本地变量,要确保不会逸出ThreadLocalThreadLocal是维护线程限制的更规范更有效的方式提供get和set操作每个get会返回有当前线程set的最新值作用防止在单例或全局变量中出现(不正确的)共享在当前线程中上下文传递变量原理Thread对象中有ThreadLocalMapThreadLocal,Var每次调用threadLocal.get()时ThreadcurrentThread=Thread.currentThread();ThreadLocalMapthreadLocalMap=currentThread.threadLocalMapVarvar=threadLocalMap.get(threadLocal)returnvar.value;不可变对象不可变对象需要满足其中的变量不能在创建后被修改所有的变量都是final类型虽然可以不全部都是final,比如String类型中的hashcode变量,这是因为它的结果来源于其他不可变变量。但是强烈不建议自己这么做。被正确创建没有发生this引用的逸出比如在构造函数中启动一个线程典型地GoogleGuava中的ImmutableXXXFinal保证在构造函数完成后,Java保证Final变量对非当前线程都是可见的。然而对于non-final变量不作此保证。?i,j等于多少安全发布对象正确创建的对象可以通过如下条件安全地发布通过静态方法初始化对象的引用将对象的引用存储到volatile或AtomicReference将对象的引用存储到正确的对象的final域中将对象的引用存储到由锁正确保护的域中例如如果将上例中的FinalFieldExample的实例的引用存储到Vector中,则可以安全地发布到通过Vector来获取它的线程中准不可变对象如果一个对象在技术上不是不可变的,但是它的状态在发布后不会再被修改,则成为准不可变对象对象的发布不可变对象可以任意发布准不可变对象必须安全地发布可变对象必须安全地发布,同时必须是线程安全的或者是被锁保护的共享对象的有效策略线程限制一个对象被限制在线程中,且被线程独占,且只能被这个线程修改共享只读一个对象在没有额外的同步下,被多个线程并发地访问,但是不能被任何线程修改。这个对象可以是可变对象或准不可变对象共享线程安全一个对象在内部进行同步,所以其他线程无需额外同步被守护一个对象只能通过特定的锁来访问。活跃度风险死锁两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象饥饿某线程请求所需资源时,总是被拒绝,以至于不能继续进行。弱响应性由于任务的耗时较长,会导致响应变慢活锁尽管没有被阻塞,但是线程仍然无法继续进行,因为在不断充实相同的操作,但却总是失败死锁的必要条件互斥条件资源只能被一个线程所使用,如果其他线程需要申请此资源,需要等待请求和保持条件线程已经保持了至少一个资源,同时又请求新的资源,但改资源又被其他线程所有,导致请求过程被阻塞,但是又不释放保持的资源不剥夺条件线程保持的资源只能由自己释放,不能被其它线程或系统剥夺环路等待条件存在线程,资源的环路,即存在P0,P1,P2,…,Pn链,其中P0在等待P1释放资源,…,Pn在等待P0释放资源无法避免无法避免避免死锁如果所有线程以固定的顺序获取锁,则不会产生死锁破坏环路等待条件尽量使用开发调用当调用的方法不需要持有锁时,成为开放调用尝试定时的锁使用ReentrantLock.tryLock(timeout)可以使用线程转储分析死锁kill-3pidjstackpid饥饿CPU周期饥饿CPU周期是典型的最常见的引发饥饿的资源在Java中,使用线程优先级不当,有可能会造成饥饿建议不要使用线程的优先级,因为会增加平台的依赖性,从而有可能导致饥饿问题。非公平性分配资源NonFair锁使用优先级队列来处理任务活锁场景一描述在消息处理程序中,如果消息处理失败后,回退整个事务,并将之置于队首。如果某消息一直处理失败(可能是BUG),则可能会导致处理程序无法继续,也即发生活锁。原因误将不可修复错误当作可修复错误解决方法1.添加重试次数上限2.不回退到队首场景二描述多个协作线程中,每个线程在请求资源时,如果发现有其他线程也在请求,则谦让地让别的线程先拥有,则有可能一直在谦让,从而导致活锁解决方法1.采取竞争的方式,而不是谦让的方式。先到先服务。2.重试时,添加随机等待。VolatileVolatile变量直接在主内存中修改所有的修改都是立刻可见的Volatile变量写操作会与所有读操作同步对volatilelong/double类型的读写都是原子性的Volatile的操作是不可重排序的volatile只针对变量的引用,而不是它代表的值volatileint[]array,则只针对array,而不会针对array中的元素可以使用AtomicXXXArrayvolatiledoublecheck如果没有volatile,则•虽然A6安全发布•但是A3不是安全引用•A2可能会为0非完整引用synchronizedJava的关键字可以对成员方法使用也可以对方法块使用必须有一个监视对象Java保证在执行同步块之后释放锁即使内部抛出异常可以配合wait/notify机制可以重进入缺点:不灵活,如无法解决读写者问题无法判断是否可以获取锁(即tryLock)无法在获取锁指定超时时间成本高显式锁Java5.0
本文标题:Java并发编程实践
链接地址:https://www.777doc.com/doc-5987877 .html