Java厉善信息-多线程-并发编程讲解_第1页
Java厉善信息-多线程-并发编程讲解_第2页
Java厉善信息-多线程-并发编程讲解_第3页
Java厉善信息-多线程-并发编程讲解_第4页
Java厉善信息-多线程-并发编程讲解_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、java多线程并发编程一、多线程1、操作系统有两个容易混淆的概念,进程和线程。进程:一个计算机程序的运行实例,包含了需要执行的指令;有自己的独立地址空间,包 含程序内容和数据;不同进程的地址空间是互相隔离的;进程拥有各种资源和状态信息, 包括打开的文件、子进程和信号处理。线程:表示程序的执行流程,是cpu调度执行的基本单位;线程有自己的程序h数器、寄 存器、堆栈和帧。同一进程屮的线程共川相同的地址空间,同时共享进进程锁拥有的内存 和其他资源。2、java标准库提供了进程和线程相关的api,进程主要包括表示进程的java. lang. process 类和创建进程的 java.iang.proc

2、essbuilder 类;表示线程的是java.iang.thread类,在虚拟机启动之g,通常只有java类的main方法这 个普通线程运行,运行时可以创建和启动新的线程;还有一类守护线程(damon thread), 守护线程在后台运行,提供程序运行时所需的服务。当虚拟机中运行的所有线程都是守护 线程时,虚拟机终止运行。3、线程间的可见性:一个线程对进程中共享的数据的修改,是否对另一个线程可见 可见性问题:a、cpu采用时间片轮转等不同算法来对线程进行调度public class idgenerator private int value = 0; public int getnext()

3、return veilue+;对于idgenerator的getnexto方法,在多线程下不能保证返回值是不重复的:各个线程之 间相互竞争cpu吋间来获取运行机会,cpu切换可能发生在执行间隙。以上代码getnext()的指令序列:cpu切换可能发生在7条指令之间,多个getnext的指令 交织在一起。1 aload_02 dup3 getfield #124 dup xl5 iconstl6 iadd7 putfield #12b、cpu缓存:r前cpu 般采用层次结构的多级缓存的架构,有的cpu提供了 l1、l2和l3三级缓存。 当cpu需要读取主存屮某个位賈的数据时,会一次检查各级缓存巾

4、是否存在对应的数裾。如果有,直接从缓存屮读取,这比从主存中读取速度快很多。当cpu需要写入时,数据先 被写入缓存中,之后再某个吋间点写回主存。所以某些时间点上,缓存中的数据与主存中 的数据可能是不一致。c、指令顺序重排出行性能考虑,编译器在编译时可能会对字节代码的指令顺序进行重新排列,以优化指令 的执行顺序,在单线程中不会有问题,但在多线程可能产生与可见性相关的问题。二、java 内存模型(java memory model)屏蔽了 cpu缓存等细节,只关注主存中的共享变量;关注对象的实例域、静态域和数组 元素;关注线程间的动作。1、volatile关键词:用来对共享变量的访问进行同步,上一次

5、写入操作的结果对下一次 读取操作是肯定可见的。(在写入volatile变量值之后,cpu缓存巾的内容会被写冋内存; 在读取volatile变量时,cpu缓存中的对应内容会被置为失效,重新从主存中进行读取), volatile不使用锁,性能优于synchronized关键词。用来确保对一个变量的修改被正确地传播到其他线程中。例子:a线程是worker,一直跑循环,b线程调川setdone(true),a线程即停止任务1 public class worker2 private volatile boolean done;3 public void setdone(boolean done)4 t

6、his, done = done;5678 91011public void work()whi le(!done) /执行任务;例子:错误使用。因为没有锁的支持,volatile的修改不能依赖于当前值,当前值可能在其 他线程中被修改。(worker是直接赋新值与当前值无关)2345public volatile static int count = 0; public static void inc() /这里延迟1毫秒,使得结果明显 try 6 thread, sleep (1);7 catch (interruptedexception e) 8 9 count+;10 11 publi

7、c static void main(string args) 12 /同时启动1000个线程,去进行i+计算,看看实际结果13 for (int i = 0; i < 1000; i+) 14 new thread(new runnable() 15 ©override16 public void run() 17 counter, inc ();18 19 ).start ();20 21 /这里每次运行的值都有可能不同,可能不为100022 system,out. println(运行结果.counter. count=+counter, count);23 24 2、f

8、inal关键词final关键词声明的域的值只能被初始化一次,一般在构造方法中初始化。(在多线程开 发中,final域通常用来实现不可变对象)当对象中的共享变量的值不可能发生变化时,在多线程中也就不耑要同步机制來进行处理, 故在多线程开发屮应尽可能使用不可变对象。另外,在代码执行时,final域的值可以被保存在寄存器屮,而不用从主存中频繁重新读取。 3、java基本类型的原子操作1)基本类型,引用类型的复制引用是原子操作;(即一条指令完成)2)long与double的赋值,引用是可以分割的,非原子操作;3)要在线程间共享long或double的字段时,必须在synchronized屮操作,或是声

9、明成 volatile三、java提供的线程同步方式1、synchronized 关键字方法或代码块的互斥性来完成实际上的一个原子操作。(方法或代码块在被一个线程调用 时,其他线程处于等待状态)所有的java对象都有一个与synchronzied关联的监视器对象(monitor),允许线程在该 监视器对象上进行加锁和解锁操作。a、静态方法:java类对应的class类的对象所关联的监视器对象。b、实例方法:当前对象实例所关联的监视器对象。c、代码块:代码块声明屮的对象所关联的监视器对象。注:当锁被释放,对共享变量的修改会写入主存;当活得锁,cpu缓存中的内界被置为无 效。编译器在处理synch

10、ronized方法或代码块,不会把其屮包含的代码移动到 synchronized方法或代码块之外,从而避免了由于代码重排而造成的问题。例:以下方法getnext()和getnextv2()都获得了当前实例所关联的监视器对象1 public class synchronizedtdgenerator2 private int value =0;3 publicsynchronizedintgetnext()4 return value+;5 6 publicint getnextv2()7 synchronized(this) 8 return value+;9 10 11 2、object 类

11、的 wait、notify 和 notifyaii 方法生产者和消费者模式,判断缓冲区是否满来消费,缓冲区是否空来生产的逻辑。如果用 while和volatile也可以做,不过本质上会让线程处于忙等待,占用cpu时间,对性能造成 影响。wait:将当前线程放入,该对象的等待池屮,线程a调用了 b对象的wait()方法,线程a 进入b对象的等待池,并且释放b的锁。(这里,线程a必须持有b的锁:,所以调用的代码必须在 synchronized 修饰下,否则直接抛出 java.iang.lllegalmonitorstateexception 异常)。notify:将该对象中等待池中的线程,随机选取

12、一个放入对象的锁池,当当前线程结來后释 放掉锁,锁池中的线程即可竞争对象的锁来获得执行机会。 notifyaii:将对象中等待池中的线程,全部放入锁池。(notify锁唤醒的线程选择由虚拟机实现來决定,不能保证一个对象锁关联的等待集合中 的线程按照所期望的顺什被唤醒,很可能一个线程被唤醒之后,发现他所要求的条件并没 有满足,而重新进入等待池。因为当等待池中包含多个线程时,一般使用notifyaii方法, 不过该方法会导致线程在没有必要的情况下被唤醒,之后又马上进入等待池,对性能有影 响,不过能保证程序的正确性)工作流程:a、consumer线程a来看产品,发现产品为空,凋用产品对象的wait(

13、),线程a进入产 品对象的等待池并释放产品的锁。b、producer线程b获得产品的锁,执行产品的notifyaii(), consumer线程a从产品的等 待池进入锁池,producer线程b生产产品,然后退出释放锁。c、consumer线程a获得产品锁,进入执行,发现有产品,消费产品,然后退出例子:1 public synchronized string pop() 2 this. notifyaho;/唤醒对象等待池中的所冇线程,可能唤醒的就是 生产者(当生产者发现产品满,就会进入对象的等待池,这里代码省略,基本 略同)3 while(index = -1) /如果发现没产品,就释放锁,

14、进入对象等待池4 this, wait ();5 /当生产者生产完后,消费者从this, wait ()方法再开始执行,第一次 还会执行循环,万一产品还是为空,则再等待,所以这里必须用while循环, 不能用if6 string good = bufferindex;7 bufferindex = null;8 index;9 return good;/消费完产品,退出。10 注:wait()方法有超时和不超时之分,超时的在经过一段时间,线程还在对象的等待池中, 那么线程也会推出等待状态。3、线程状态转换:已经废弃的方法:stop、suspend、resume、destroy,这些方法在实现上

15、时不安全的。线程的状态:new、runnable、blocked、waiting、timed_waiting (有超时的等 待)、terminated。a、方法sleep()进入的bi塞状态,不会释放对象的锁(即大家一起睡,谁也别想执行代 码),所以示i让sleep方法处在synchronized方法或代码块屮,否则造成其他等待获取 锁的线程忪时间处于等待。b、方法joino则是主线程等待子线程完成,再往下执行。例如main方法新建两个线程a 和bpublic static void main(string args) throws interruptedexception thread tl

16、 = new thread (new threadtesterao);thread t2 = new thread (new threadtesterbo); tl. start ();tl. join() ; /等tl执行完再往下执行t2. start ();t2. joino; /在虚拟机执行屮,这句可能被忽略c、方法interrupt(),向被调用的对象线程发起屮断请求。如线程a通过调用线程b的d的 interrupt方法来发出中断请求,线程b来处理这个请求,当然也可以忽略,这不是必须的。 object类的wait()、thread类的join()和sleep方法都会抛出受检异常java

17、.lang. interrupted exception,通interrupt方法中断该线程.会导致线稅离开等待状态。对于wait()调用来说,线程需要重新获取监视器对象上的锁之后才能抛hiinterruptedexception异常,并致以异常的处理逻辑。可以通过thread类的islnterrupted方法来判断是否有中断请求发生,通常可以利用这个 方法來判断是否退出线程(类似上面的volatitle修饰符的例子);thread类还有个方法lnterrupted(),该方法不但可以判断当前线程是否被中断,还会清楚 线程内部的屮断标记,如果返回true,即曾被请求中断,同时调用完后,淸除巾断

18、标记。如果一个线程在某个对象的等待池,那么notify和interrupt都可以使该线程从等待池中被 移除。如果同时发生,那么看实际发生顺序。如果是notify先,那照常唤醒,没影响。如 果是interrupt先,并且虚拟机选择让该线程中断,那么即使nofity,也会忽略该线程,而 唤醒等待池中的另一个线程。e、yield(),尝试让出所占有的cpu资源,让其他线程获取运行机会,对操作系统上的调 度器来说是一个信号,不一定立即切换线程。(在实际开发中,测试阶段频繁调用yeid方 法使线程切换更频繁,从而让一些多线程相关的错误更容易暴露出來)。四、非阻塞方式 线程之间同步机制的核心是监视对象上的

19、锁,竞争锁來获得执行代码的机会。当一个对象 获取对象的锁,然后其他尝试获取锁的对象会处于等待状态,这种锁机制的实现方式很大 程度限制了多线程程序的吞叶景和性能(线程阻塞),且会带来死锁(线程a有a对象锁, 等着获取b对象锁,线程b有b对象锁,等待获取a对象锁)和优先级倒罝(优先级低的 线程获得锁,优先级高的只能等待对方释放锁)等问题。如果能不阻塞线程,又能保证多线程程序的正确性,就能有更好的性能。在程序屮,对共享变量的使用一般遵循一定的模式,即读取、修改和写入三步组成。之前 碰到的问题是,这三步执行屮可能线程执行切换,造成非原子操作。锁机制是把这三步变 成一个原子操作。目前cpu本身实现将这三

20、步合起來形成一个原子操作,无需线程锁机制干预,常见的指 令是“比较和替换” (compare and swap,cas),这个指令会先比较某个内存地址的当前依 是不是指定的旧指,如果是,就用新值替换,否则什么也不做,指令返冋的结果是内存地 址的当前值。通过cas指令可以实现不依赖锁机制的阻塞算法。一般做法是把cas指令 的调用放在一个无限循环中,不断尝试,知道cas指令成功完成修改。java.util.concurrent.atomic包中提供了 cas指令。(不是所有cpu都文持cas,在某些平 台,java.util.concurrent.atomic的实现仍然是锁机制) atomic包中

21、提供的java类分成三类:1、支持以原子操作来进行更新的数据类型的java类(atomicboolean、atomiclnteger、 atomicreference),在内存模型相关的语义上,这四个类的对象类似于volatile变量。类屮的常用方法:a、compareandset:接受两个参数,一个是期望的旧值,一个是替换的新值。b、weakcompareandset:效果同compareandset (jsr屮表示weak原子力式渎収和有 条件地写入变量但不创建任何happen-before排序,但在源代码中和compareandset完企一样,所以并没有按jsr实现)c、get和set:

22、分别用来直接获取和设賈变:w:的值d、lazyset:与set类似,但允许编译器把lazyset方法的调用与后而的指令进行重排,因 此对值得设置操作有可能被推迟。例:1 public class atomicidgenerator2 private final atomiclnter counter = new atomiclnteger(0);3 public int getnext()4 return counter. getandlncrement();5 6 7 / getandtncrement方法的内部实现方式,这也是cas方法的一般模式,cas方法不一定成功,所以包装在一个无限循

23、环屮,直到成功8 public final int getandlncrement()9 for(; ;) 10 int current = get();11 int next = current +1;12 if (compareandset(current, next)13 return current;14 15 2、提供对数组类型的变量进行处理的java类,atomicintegerarray、atomiclongarray和atomicreferencearray类。(同上,只是放去类数组里,调用时也只是多了一个操作元素索引的参数)3、通过反射的方式对任何对象中包含的volatitl

24、e变量使用cas方法,atomicintegerfieldupdater、atomiclongfieldupdater、atomicreferencefieldupdater。他 们提供了一种方式把cas的功能扩展到了任何java类巾声明为volatitle的域上。(灵活, 但语义较弱,因为对象的volatitle可能被非atomic的其他方式被修改)1 public class treenode2 private volatile treenode parent;3 /静态工厂方法4 private static final atomicreferencefieldupdater<tr

25、eenode, treenode> parentupdater =atomicrcfcrcnccficldupdatcr. ncwupdatcr(trccnodc. class, trccnodc. class, "parent");5 public boolean compareandsetparent(treenode expect, treenode update)6 return parentupdater. compareandset(this, expect, update);7 8 注:java.util.concurrent.atomic包屮的java

26、类j,4于比较底层的实现,一般作为java.util.concurrent包中很多非阻塞的数据纟s构的实现基础。比$交多的川 atomicboolean、 atomiclnteger、 atomiclong 和 atomicreference。在实现线程&全的计数器时,atomiclnteqer和atomiclonq类时最佳的选择。五、高级同步机制(比synchronized更灵活的加锁机制)synchronized和volatile,以及wait、notify等力法抽象层次低.在s序开发巾使川比较繁琐,易出错。而多线程之间的交互来说,存在某些同定的模式,如生产者-消费者和读者-写者模

27、式,把 这些模式抽象成高层api,使用起来会非常方便。java.util.concurrent包为多线程提供丫高层的api,满足日常开发巾的常见耑求。常用接口1、lock接口,表示一个锁方法:a、lock(),获取所,如果无法获取所锁,会处于等待状态b、unlock(),释放锁。(一般放在finally代码块中)c、locklnterruptibly(),与lock()类似,但允许当前线程在等待获取锁的过程中被中断。 (所以要处理 interruptedexception)d、trylock(),以非阻滋方式获取锁,如果无法获取锁,则返回false。(trylocko的另一 个重载可以指定超时

28、,如果指定超时,当无法获取锁,会等待而阻塞,同时线程可以被中 断)2、readwritelock接口,表示两个锁,读取的共享锁和写入的排他锁。(适合常见的 读者-写者场景)readwritelock接门的readlock和writelock方法来获取对应的锁的lock接门的实现。在多数线程读取,少数线程写入的情况下,可以提高多线程的性能,提高使川该数据结构 的吞吐量。如來是相反的情况,较多的线程写入,则接口会降低性能。3、reentrantlock类和reentrantreadwritelock,分别为上面两个接口的实现类。他们具有重入性:即允许一个线程多次获収同一个锁(他们会记住上次获収锁并

29、且未释放 的线程对象,和加锁的次数,getholdcount()同一个线程每次获取锁,加锁数+1,每次释放锁,加锁数-1,到0,则该锁被释放,可以 被其他线程获取。1 public class locktdgenrator2 /new rccntrantlock(true)是重载,使用更加公平的加锁机制,在锁被释 放后,会优先给等待时间最长的线程,避免一些线程长期无法获得锁:3 private int reentrantlock lock = reentrantlock ();4 privafte int value = 0;5 public int getnext()678 910lock,

30、 lock 0;/进来就加锁,没有锁会等待try return value+;/实际操作 finally1 ock. unlock() ;/释放锁11 12 13 啦注:重入性减少了锁在各个线程之间的等待,例如便利一个hashmap,每次next()之前加 锁,之后释放,可以保证一个线程一门气完成便利,而不会每次nexto之后释放锁,然后 和其他线程竞争,降低了加锁的代价,提供了程序整体的吞吐量。(即,让一个线程一口 气完成任务,再把锁传递给其他线程)。4、condition 接口,lock 接口代替了 synchronized, condition 接口替代了 object 的 wait、

31、nofity。a、await(),使当前线程进入等待状态,知道被唤醒或中断。重载形式可以指定超时时间。b、awaitnanos(),以纳秒为单位等待。c、awaituntil(),指定超吋发生的吋间点,而不是经过的吋间,参数为java.util.date。d、awaituninterruptibly(),前面几种会响应其他线程发出的巾断请求,他会无视,直到被 唤醒。注:与object类的wait()相同,await()会释放其所持有的锁。e、signal()和 signalaii,相当于 notify 和 notifyaii1 lock lock = new reentrantlock ();

32、2 condition condition = lock. newcondition();3 lock. locko ;4 try 5 while(/*逻辑条件不满足*/) 6 condition, await ();7 8 finally9 lock, unlock ();10 六、底层同步器多线程程序中,线程之间存在多种不同的同步方式。除了 java标准库提供的同步方式之外, 程序屮特有的同步方式需要由开发人员自己来实现。常见的一种需求是对有限个共享资源的访比如多台个人电脑,2台打印机,当多个线 程在等待同一个资源时,从公平角度出发,会用fifo队列。如果程序中的同步方式可以抽象成对有限个

33、资源的访问,那么可以使用java.util.concurrent.locks 包中的 abstractqueuedsynchronizer 类和abstractqueuedlongsynchronizer矣作为实现的基础,前者用int类型的变量来维护内部 状态,而后者用long类型。(可以将这个变量理解为共享资源个数)通过 getstate、setstate、和 compareandsetstate3 个方法更新p、j 部变量的值。abstractqueuedsynchronizer类是abstract的,需要覆盖其巾包含的部分方法,通常做法 是把其作为一个java类的内部类,外部类提供具体的

34、同步方式,内部类则作力实现的基础。 有两种模式,排他模式和共享模式,分别对应方法tryacquire(、tryrelease和 tryacquireshared、tryreleaseshared,在这些方法屮,&用 getstate、setstate、 compareandsetstate3个方法来修改内部变量的值,以此来反应资源的状态。1 public class simpleresourcemanagerprivate final tnnersynchronizer synchronizer;3 private static class inncrsynchronizcr exte

35、nds abstractqueuedsynchronizer 45678 91011121314151617181920 21 2223242526272829303132innersynchronizer(int numofresources) setstate (numofresources);protected int tryacquireshared(int acquires) for(; ;) int available = getstateo ;int remain = available - acquires;if(remain <0 | comaprcandsctstat

36、e(available, remain) return remain;protected boolean try releaseshared(int releases) for(; ;) int available = getstateo ;int next = available + releases;if (compareandsetstate(available, next) return true;public simpleresourcemanager(int numofresources)synchronizer = new innersynchronizer(numofresou

37、rces);public void acquire() throws interruptedexception synchronizer. acquiresharedlnterruptibly(1);pubic void release()33 synchronizer. releaseshared(1);34 35 七、高级同步对象(提高开发效率)atomic和locks包提供的java类可以满足基本的互斥和同步访w的耑求,但这些java类 的抽象层次较低,使用比较复杂。更简甲的做法是使用java.util.concurrent包屮的高级同步对象。1、信号量。信号量一般用來数量有限的资源,每

38、类资源有一个对象的信号量,信号量的值表示资源的 可用数量。在使用资源时,需要从该信号:w:上获取许可,成功获取许可,资源的可用数-1;完成对资 源的使用,释放许可,资源可用数+1;当资源数为0吋,需要获取资源的线程以阻塞的 方式來等待资源,或过段时间之后再来检查资源是否可用。(上面的simpleresourcemanager类实际上时信号景的一个简单实现)java.util.concurrent.semaphore类,在创建semaphore类的对象时指定资源的可用数a、acquire(),以阻塞方式获取许可b、tryacquire(),以非肌塞方式获取许可c、release(),释放许可。d

39、、accquireuninterruptibly(), accquire()方法获取许可以的过程可以被巾断,如果不希望被中断,使用此方法。1 public class printermexnager2 private final semphore semaphore;3 private final list<printer> printers = new arraylisto ():4 public printermanager(collection<? extends printer printers)5 this.printers. addall(printers);6

40、/这里重载方法,第二个参数为true,以公平竞争模式,防止线程 饥饿7 this, semaphore = new semaphore(this. printers, size(), true);8 9 public printer acquircprintcr() throws interruptcdexccption10 semaphore, acquire();11 return getavailableprinter();12 13 publicvoidreleaseprinter(printerprinter)14 putbackprinter(pinter);15 semaphor

41、e, release ();16 17 privatesynchronizedprintergetavailablcprintero 18 printer result = printers.get(0);19 printers, remove(0);20 return result;21 22 privatesynchronized void putbackprinter(printer printer) 23 printers, add(printer);24 25 2、倒数闸门多线程协作时,一个线程等待另外的线程完成任务才能继续进行。java.util.concurrent.countd

42、ownlatch类,创建该类时,指定等待完成的任务数;当一个任 务完成,调用countdonw(),任务数-1。等待任务完成的线程通过await(),进入阻塞状态, 直到任务数w:为0。countdownlatch类为一次性,一旦任务数为0,再调用await()不再 阻塞当前线程,直接返回。例:41 public class pagesizesorter2 /并发性能远远优于hashtable的map实现,hashtable做任何操作都 需耍获得锁,同一时间只有有个线程能使用,而concurrenthashmap是分段加 锁,不同线程访问不同的数据段,完全不受影响,忘记hashtable吧。3

43、 private static final concurrenthashmapstring , interger> sizemap = new concurrenthashmapo ();4 private static class getsi zeworker implements runnable 5 private final string urlstring;6 public gctsizeworkcr(string urlstring , countdownlatch signal)78 91011121314151617181920 21this.urlstring = ur

44、lstirng; this.signal = signal;public void run() try inputstrcam is = new url (urlstring). openstrcamo ; int size = ioutils.tobytearray(is). length; sizemap. put(urlstring, size);catch(loexception e)sizemap. put(urlstring, -1);finallysignal. countdown() :/完成一个任务,任务数-1private void sort () 23 list<e

45、ntry<string, integer list = newarraylist<sizemap. entryset ();24 collections, slort(list, newcomparator<rntry<string, integer()25 public int compare (entry<string, integer ol,entry<sting , integer o2)26 return integer, compare (o2. getvalue(), ol. getvalue ();27 ;28 system, out. pr

46、intln(arrays. deeptostring(list. toarray();29 30 public void sortpagesize(col 1ection<string> urls) throws interruptedrxception31 countdownlatch sortsignal = new countdownlatch(urls. sizco);32 for (string url: urls) 33 new thread (new getsizeworker(url, sortsignal). start ();34 35 sortsignal.

47、await () :/主线程在这里等待,任务数归0,则继续执 行36 sort ();37 38 3、循环屏障循环屏障在作用上类似倒数闸门,不过他不像倒数闸门是一次性的,可以循环使用。另外, 线程之间是互相平等的,彼此都需要等待对方完成,当一个线程完成自己的任务之后,等 待其他线程完成。当所有线程都完成任务之后,所有线程才可以继续运行。当线程之间需要再次进行互相等待时,可以复用同一个循环屏障。类java.uti.concurrent.cyclicbarrier用来表示循环屏障,创建时指定使用该对象的线程数目,还可以指定一个runnable接口的对象作为每次循环后执行的动作。(当最后一个线程完

48、成任务之后,所有线程继续执行之前,被执行。如果线程之间盂要更新一些共享的闪部状 态,可以利用这个runnalbe接口的对象來处理)。每个线程任务完成之后,通过调用await方法进行等待,当所有线程都调用await方法之 后,处于等待状态的线程都可以继续执行。在所有线程中,只要有一个在等待屮被屮断, 超吋或是其他错误,整个循环屏障会失败,所有等待中的其他线程抛山java.uti.concurrent巳 rokenbarrierexception。例:每个线程负贵找一个数字区间的质数,当所有线程完成后,如果质数数n不够,继续 扩大范围查找1 public class primenumber2345

49、private static final int totalcoutn = 5000; private static final int range_length= 200; private static final int worker_number = 5; private static volatitle boolean done = false;6privatestatic int rangecount =0;7privatestatic final list<long> results = new arraylist<long>()8privatestatic

50、 final cyclicbarrier barrier = newcyclicbarrier (worker_number, new runnable () 9public void run() 10if (results. sizeo >= total_count) 11done = true;121314);15privatestatic class primefinder implements runnable16public void run() 17while(!done) /整个过程在一个while循环下,awaito等待下次循环开始,会再次判断执行条件18int rang

51、e = getnextrange();19long start = rang * range length;20long end = (range + 1) * rangelength;21for (long i = start; i<end;i+)22if (isprime(i) 23updateresult(i);242526try 27barrier, await ();28catch (interruptedexceptionbokenbarrierexceptione)29done = true;3031323334private synchronized static voi

52、d updatcrcsult (long value)35results, add (value);3637privatesynchronized static int getnextrange0 38return rangecount+;3940privatestatic boolean isprime(long number)41/找质数的代码4243public void calculate()44for(int i=0;i<worker_number;i+) 45new thread (new primefinder(). start();4647 while(!done)484

53、8 49 /计算完成50 51 4、对象交换器适合于两个线程需要进行数据交挽的场景。(一个线程完成后,把结果交给另一个线程继 续处理)java.util.concurrent.exchanger类,提供了这种对象交换能力,两个线程共享一个 exchanger类的对象,一个线程完成对数裾的处理之后,调用exchanger类的exchange() 方法把处理之后的数据作力参数发送给另外-个线程。而exchange方法的返回结果是另 外一个线程锁提供的相同类型的对象。如果另外一个线程未完成对数据的处理,那么 exchanged会使当前线程进入等待状态,直到另夕i、一个线程也调用了 exchange方

54、法来进 行数据交换。例:1 public class sendandrcceivernew2 private final exchanger<stringbuildcr> exchanger exchanger<stringbuilder>();345673 910 11 1213141516171819private class sender implements runnable public void run() try stringbuilder content = new stringbuilder(hello); content = exchanger, e

55、xchange(content);catch(interruptedexception e) thread. currcntthreado. interrupt ();private class receiver implements runnable public void run() try stringbuilder content = new stringbuilder (world”); content = exchanger.exchange(content);catch(interruptedexception e)thread. currentthread (). interrupt ();public void exchange()20 21 22 2324 new thread (new s

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论