已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
多线程之间需要协调工作。例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程 downloadThread将该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了任务 后,再通知displayThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。例如:线程A:synchronized(obj) while(!condition) obj.wait(); obj.doSomething();当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:线程B:synchronized(obj) condition = true;obj.notify();需要注意的概念是:1、调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) .代码段内。即:obj.wait(), obj.notify()必须在synchronized(obj)内部,也就是说,必须得先得到锁才可以wait, nofity2、主调obj.wait()的线程(线程A),在obj.wait()执行时,释放掉锁3、线程A再次运行时,必须要得到锁,而且,这次运行是从obj.wait()后的第一条语句开始的(当然不包括obj.wait())4、如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。5、obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁(先等B自己释放锁,A1A2A3才有一个能得到),因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。6、当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。JAVA提供了新的更加健壮的线程处理机制,包括了同步、锁定、线程池等等,它们可以实现更小粒度上的控制。await()和signal()就是其中用来做同步的两种方法,它们的功能基本上和 wait()/notify()相同,完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。接口Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。 锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。不过,某些锁可能允许对共享资源并发访问,如 ReadWriteLock 的读取锁。 synchronized 方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。 虽然 synchronized 方法和语句的范围机制使得使用监视器锁编程方便了很多,而且还帮助避免了很多涉及到锁的常见编程错误,但有时也需要以更为灵活的方式使用锁。例如,某些遍历并发访问的数据结果的算法要求使用 hand-over-hand 或 chain locking:获取节点 A 的锁,然后再获取节点 B 的锁,然后释放 A 并获取 C,然后释放 B 并获取 D,依此类推。Lock 接口的实现允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁,从而支持使用这种技术。 随着灵活性的增加,也带来了更多的责任。不使用块结构锁就失去了使用 synchronized 方法和语句时会出现的锁自动释放功能。在大多数情况下,应该使用以下语句: Lock l = .; l.lock();/加锁 try / access the resource protected by this lock finally l.unlock();/解锁 锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。 Lock 实现提供了使用 synchronized 方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁尝试 (tryLock()、一个获取可中断锁的尝试 (lockInterruptibly() 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit)。 Lock 类还可以提供与隐式监视器锁完全不同的行为和语义,如保证排序、非重入用法或死锁检测。如果某个实现提供了这样特殊的语义,则该实现必须对这些语义加以记录。 注意,Lock 实例只是普通的对象,其本身可以在 synchronized 语句中作为目标使用。获取 Lock 实例的监视器锁与调用该实例的任何 lock() 方法没有特别的关系。为了避免混淆,建议除了在其自身的实现中之外,决不要以这种方式使用 Lock 实例。 除非另有说明,否则为任何参数传递 null 值都将导致抛出 NullPointerException。voidlock() 获取锁。voidlockInterruptibly() 如果当前线程未被中断,则获取锁。ConditionnewCondition() 返回绑定到此 Lock 实例的新 Condition 实例。booleantryLock() 仅在调用时锁为空闲状态才获取该锁。booleantryLock(longtime, TimeUnitunit) 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。voidunlock() 释放锁。类 ReentrantLock一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。 此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。与采用默认设置(使用不公平锁)相比,使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁和保证锁分配的均衡性时差异较小。不过要注意的是,公平锁不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。还要注意的是,未定时的 tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁是可用的,此方法就可以获得成功。 建议总是 立即实践,使用 lock 块来调用 try,在之前/之后的构造中,最典型的代码如下: class X private final ReentrantLock lock = new ReentrantLock(); / . public void m() lock.lock(); / block until condition holds try / . method body finally lock.unlock() 构造方法摘要ReentrantLock() 创建一个 ReentrantLock 的实例。ReentrantLock(booleanfair) 创建一个具有给定公平策略的 ReentrantLock。ConditionnewCondition() 返回用来与此 Lock 实例一起使用的 Condition 实例。protected CollectiongetWaitingThreads(Conditioncondition) 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。intgetWaitQueueLength(Conditioncondition) 返回等待与此锁相关的给定条件的线程估计数。booleanhasQueuedThread(Threadthread) 查询给定线程是否正在等待获取此锁。booleanhasQueuedThreads() 查询是否有些线程正在等待获取此锁。booleanhasWaiters(Conditioncondition) 查询是否有些线程正在等待与此锁有关的给定条件。接口 ConditionCondition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。 Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。 作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。 class BoundedBuffer final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object items = new Object100; int putptr, takeptr, count; public void put(Object x) throws InterruptedException lock.lock(); try while (count = items.length) notFull.await(); itemsputptr = x; if (+putptr = items.length) putptr = 0; +count; notEmpty.signal(); finally lock.unlock(); public Object take() throws InterruptedException lock.lock(); try while (count = 0) notEmpty.await(); Object x = itemstakeptr; if (+takeptr = items.length) takeptr = 0; -count; notFull.signal(); return x; finally lock.unlock(); (ArrayBlockingQueue 类提供了这项功能,因此没有理由去实现这个示例类。) Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。 注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和 notification 监视器方法。获取 Condition 实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。 实现注意事项在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。 三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。 因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。 要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。 由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。 voidawait() 造成当前线程在接到信号或被中断之前一直处于等待状态。booleanawait(longtime, TimeUnitunit) 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。longawaitNanos(longnanosTimeout) 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。voidawaitUninterruptibly() 造成当前线程在接到信号之前一直处于等待状态。booleanawaitUntil(Datedeadline) 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。voidsignal() 唤醒一个等待线程。voidsignalAll() 唤醒所有等待线程。接口ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。 所有 ReadWriteLock 实现都必须保证 writeLock 操作的内存同步效果也要保持与相关 readLock 的联系。也就是说,成功获取读锁的线程会看到写入锁之前版本所做的所有更新。 与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁(多读一写)利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。 与互斥锁相比,使用读-写锁能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用即在同一时间试图对该数据执行读取或写入操作的线程数。例如,某个最初用数据填充并且之后不经常对其进行修改的 collection,因为经常对其进行搜索(比如搜索某种目录),所以这样的 collection 是使用读-写锁的理想候选者。但是,如果数据更新变得频繁,数据在大部分时间都被独占锁,这时,就算存在并发性增强,也是微不足道的。更进一步地说,如果读取操作所用时间太短,则读-写锁实现(它本身就比互斥锁复杂)的开销将成为主要的执行成本,在许多读-写锁实现仍然通过一小段代码将所有线程序列化时更是如此。最终,只有通过分析和测量,才能确定应用程序是否适合使用读-写锁。 尽管读-写锁的基本操作是直截了当的,但实现仍然必须作出许多决策,这些决策可能会影响给定应用程序中读-写锁的效果。这些策略的例子包括: 在 writer 释放写入锁时,reader 和 writer 都处于等待状态,在这时要确定是授予读取锁还是授予写入锁。Writer 优先比较普遍,因为预期写入所需的时间较短并且不那么频繁。Reader 优先不太普遍,因为如果 reader 正如预期的那样频繁和持久,那么它将导致对于写入操作来说较长的时延。公平或者“按次序”实现也是有可能的。 在 reader 处于活动状态而 writer 处于等待状态时,确定是否向请求读取锁的 reader 授予读取锁。Reader 优先会无限期地延迟 writer,而 writer 优先会减少可能的并发。 确定是否重新进入锁:可以使用带有写入锁的线程重新获取它吗?可以在保持写入锁的同时获取读取锁吗?可以重新进入写入锁本身吗? 可以将写入锁在不允许其他 writer 干涉的情况下降级为读取锁吗?可以优先于其他等待的 reader 或 writer 将读取锁升级为写入锁吗? LockreadLock() 返回用于读取操作的锁。LockwriteLock() 返回用于写入操作的锁。示例用法。下面的代码展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理): class CachedData Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() rwl.readLock().lock();/读锁,加锁 if (!cacheValid) rwl.readLock().unlock();/读锁,解锁 rwl.writeLock().lock();/写锁,加锁(必须解除读锁才可以写) / Recheck state because another thread might have acquired / write lock and changed state before we did. if (!cacheValid) data = . cacheValid = true; rwl.readLock().lock();/读锁,加锁(写锁没解也可以读)rwl.writeLock().unlock(); / Unlock write, still hold read use(data); rwl.readLock().unlock(); 在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。 class RWDictionary private final Map m = new TreeMap(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); public Data get(String key) r.lock(); try return m.get(key); finally r.unlock(); public String allKeys() r.lock(); try return m.keySet().toArray(); finally r.unlock(); public Data put(String key, Data value) w.lock(); try return m.put(key, value); finally w.unlock(); public void clear() w.lock(); try m.clear(); finally w.unlock(); 1.sleep()使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是如果有Synchronized同步块,其他线程仍然不同访问共享数据。注意该方法要捕获异常比如有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完成后,低优先级的线程才能执行;但当高优先级的线程sleep(5000)后,低优先级就有机会执行了。总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。2.join()join()方法使调用该方法的线程在此之前执行完毕,也就是等待调用join()方法的线程执行完毕后再往下继续执行。注意该方法也要捕获异常。3.yield()它与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。4.wait()和notify()、notifyAll()这三个方法用于协调多个线程对共享数据的存取,所以必须在Synchronized语句块内使用这三个方法。前面说过Synchronized这个关键字用于保护共享数据,阻止其他线程对共享数据的存取。但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出Synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。wait()方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Synchronized数据块,当前线程被放入对象等待池中。当调用 notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中的线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。注意 这三个方法都是java.lang.Ojbect的方法!再添两天比较熟悉的:2.run()和start()这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由Java的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。2.关键字Synchronized这个关键字用于保护共享数据,当然前提是要分清哪些数据是共享数据。每个对象都
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年下半年吉林通化市通化县事业单位专项招聘17人(7号)易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年吉林省直事业单位招聘工作人员110人笔试易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年吉安市庐陵新区管委会招考社区专职工作者易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年厦门大学嘉庚学院骨干教师招考易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年南昌铁路通信信号厂限公司招聘易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年农民日报社招考事业编制驻记者站记者易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年内蒙古锡林郭勒盟事业单位招聘工作人员375人笔试易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年内蒙古通辽市卫生系统招考易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年内蒙古气象局机关服务中心招考硕士研究生易考易错模拟试题(共500题)试卷后附参考答案
- 2025年下半年六安市舒城县事业单位招考(100人)易考易错模拟试题(共500题)试卷后附参考答案
- 2025年注册兽医《兽医临床诊疗学》备考题库及答案解析
- 2025年小学五年级数学上学期单元测试专项训练(含答案)
- 2025宁夏交通建设投资集团有限公司校园招聘和社会招聘230人(1号)考试笔试备考试题及答案解析
- 2025汉中市级机关遴选公务员及选聘事业单位人员(54人)笔试考试备考试题及答案解析
- 2025广东广州市海珠区教育系统高校“优才计划”招聘68人笔试考试参考试题及答案解析
- 甘肃省陇南市西和县2025-2026学年八年级上学期周期学业能力评鉴数学试卷(含解析)
- 2025年中小学课外读物进校园落实情况自查报告
- 防爆弹课件教学课件
- 2025年护理专业单招试题及答案
- 电梯基础知识培训教程课件
- 气质联用仪课件
评论
0/150
提交评论