Java线程同步方法实例总结
本文实例讲述了Java线程同步方法。分享给大家供大家参考,具体如下:
1.Semaphore
1.1二进制Semaphore
Semaphore算是比较高级点的线程同步工具了,在许多其他语言里也有类似的实现。Semaphore有一个最大的好处就是在初始化时,可以显式的控制并发数。其内部维护这一个c计数器,当计数器小于等于0时,是不允许其他线程访问并发区域的,反之则可以,因此,若将并发数设置为1,则可以确保单一线程同步。下面的例子模拟多线程打印,每个线程提交打印申请,然后执行打印,最后宣布打印结束,代码如下:
importjava.util.concurrent.Semaphore; publicclassProgram{ publicstaticvoidmain(String[]agrs){ PrintQueuep=newPrintQueue(); Thread[]ths=newThread[10]; for(inti=0;i<10;i++){ ths[i]=newThread(newJob(p),"Thread"+i); } for(inti=0;i<10;i++){ ths[i].start(); } } } classPrintQueue{ privateSemaphores; publicPrintQueue(){ s=newSemaphore(1);//二进制信号量 } publicvoidprintJob(Objectdocument){ try{ s.acquire(); longduration=(long)(Math.random()*100); System.out.printf("线程名:%s睡眠:%d",Thread.currentThread().getName(),duration); Thread.sleep(duration); } catch(InterruptedExceptione){ e.printStackTrace(); } finally{ s.release(); } } } classJobimplementsRunnable{ privatePrintQueuep; publicJob(PrintQueuep){ this.p=p; } @Override publicvoidrun(){ System.out.printf("%s:正在打印一个任务\n",Thread.currentThread().getName()); this.p.printJob(newObject()); System.out.printf("%s:文件已打印完毕\n",Thread.currentThread().getName()); } }
执行结果如下:
Thread0:正在打印一个任务
Thread9:正在打印一个任务
Thread8:正在打印一个任务
Thread7:正在打印一个任务
Thread6:正在打印一个任务
Thread5:正在打印一个任务
Thread4:正在打印一个任务
Thread3:正在打印一个任务
Thread2:正在打印一个任务
Thread1:正在打印一个任务
线程名:Thread0睡眠:32 Thread0:文件已打印完毕
线程名:Thread9睡眠:44 Thread9:文件已打印完毕
线程名:Thread8睡眠:45 Thread8:文件已打印完毕
线程名:Thread7睡眠:65 Thread7:文件已打印完毕
线程名:Thread6睡眠:12 Thread6:文件已打印完毕
线程名:Thread5睡眠:72 Thread5:文件已打印完毕
线程名:Thread4睡眠:98 Thread4:文件已打印完毕
线程名:Thread3睡眠:58 Thread3:文件已打印完毕
线程名:Thread2睡眠:24 Thread2:文件已打印完毕
线程名:Thread1睡眠:93 Thread1:文件已打印完毕
可以看到,所有线程提交打印申请后,按照并发顺序一次执行,没有任何并发冲突,谁先获得信号量,谁就先执行,其他剩余线程均等待。这里面还有一个公平信号与非公平信号之说:基本上java所有的多线程工具都支持初始化的时候指定一个布尔变量,true时表明公平,即所有处于等待的线程被筛选的条件为“谁等的时间长就选谁进行执行”,有点firstinfirstout的感觉,而false时则表明不公平(默认是不non-fairness),即所有处于等待的线程被筛选执行是随机的。这也就是为什么多线程往往执行顺序比较混乱的原因。
1.2多重并发控制
若将上面的代码改为s=newSemaphore(3);//即让其每次可以并发3条线程,则输出如下:
Thread0:正在打印一个任务
Thread9:正在打印一个任务
Thread8:正在打印一个任务
Thread7:正在打印一个任务
Thread6:正在打印一个任务
Thread5:正在打印一个任务
Thread3:正在打印一个任务
Thread4:正在打印一个任务
Thread2:正在打印一个任务
Thread1:正在打印一个任务
线程名:Thread9睡眠:26线程名:Thread8睡眠:46线程名:Thread0睡眠:79 Thread9:文件已打印完毕
线程名:Thread7睡眠:35 Thread8:文件已打印完毕
线程名:Thread6睡眠:90 Thread7:文件已打印完毕
线程名:Thread5睡眠:40 Thread0:文件已打印完毕
线程名:Thread3睡眠:84 Thread5:文件已打印完毕
线程名:Thread4睡眠:13 Thread4:文件已打印完毕
线程名:Thread2睡眠:77 Thread6:文件已打印完毕
线程名:Thread1睡眠:12 Thread1:文件已打印完毕
Thread3:文件已打印完毕
Thread2:文件已打印完毕
很明显已经并发冲突了。若要实现分组(每组3个)并发吗,则每一组也要进行同步,代码修改如下:
importjava.util.concurrent.Semaphore; importjava.util.concurrent.locks.Lock; importjava.util.concurrent.locks.ReentrantLock; publicclassProgram{ publicstaticvoidmain(String[]agrs){ PrintQueuep=newPrintQueue(); Thread[]ths=newThread[10]; for(inti=0;i<10;i++){ ths[i]=newThread(newJob(p),"Thread"+i); } for(inti=0;i<10;i++){ ths[i].start(); } } } classPrintQueue{ privateSemaphores; privateboolean[]freePrinters; privateLocklock; publicPrintQueue(){ s=newSemaphore(3);//二进制信号量 freePrinters=newboolean[3]; for(inti=0;i<3;i++){ freePrinters[i]=true; } lock=newReentrantLock(); } publicvoidprintJob(Objectdocument){ try{ s.acquire(); intprinterIndex=getIndex(); longduration=(long)(Math.random()*100); System.out.printf("线程名:%s睡眠:%d\n",Thread.currentThread().getName(),duration); Thread.sleep(duration); freePrinters[printerIndex]=true;//恢复信号,供下次使用 } catch(InterruptedExceptione){ e.printStackTrace(); } finally{ s.release(); } } //返回一个内部分组后的同步索引 publicintgetIndex(){ intindex=-1; try{ lock.lock(); for(inti=0;i其中getIndex()方法主要为了维护内部分组后(支持并发3个)组内数据的同步(用lock来同步)。
输出如下:
Thread0:正在打印一个任务
Thread9:正在打印一个任务
Thread8:正在打印一个任务
Thread7:正在打印一个任务
Thread6:正在打印一个任务
Thread5:正在打印一个任务
Thread4:正在打印一个任务
Thread3:正在打印一个任务
Thread2:正在打印一个任务
Thread1:正在打印一个任务
线程名:Thread0睡眠:82 打印机:0号
线程名:Thread8睡眠:61 打印机:2号
线程名:Thread9睡眠:19 打印机:1号
Thread9:文件已打印完毕
线程名:Thread7睡眠:82 打印机:1号
Thread8:文件已打印完毕
线程名:Thread6睡眠:26 打印机:2号
Thread0:文件已打印完毕
线程名:Thread5睡眠:31 打印机:0号
Thread6:文件已打印完毕
线程名:Thread4睡眠:44 打印机:2号
Thread7:文件已打印完毕
线程名:Thread3睡眠:54 打印机:1号
Thread5:文件已打印完毕
线程名:Thread2睡眠:48 打印机:0号
Thread4:文件已打印完毕
线程名:Thread1睡眠:34 打印机:2号
Thread3:文件已打印完毕
Thread2:文件已打印完毕
Thread1:文件已打印完毕2.CountDownLatch
CountDownLatch同样也是支持多任务并发的一个工具。它主要用于“等待多个并发事件”,它内部也有一个计数器,当调用await()方法时,线程处于等待状态,只有当内部计数器为0时才继续(countDown()方法来减少计数),也就说,假若有一个需求是这样的:主线程等待所有子线程都到达某一条件时才执行,那么只需要主线程await,然后在启动每个子线程的时候进行countDown操作。下面模拟了一个开会的例子,只有当所有人员都到齐了,会议才能开始。
importjava.util.concurrent.CountDownLatch; publicclassProgram{ publicstaticvoidmain(String[]agrs){ //开启可容纳10人的会议室 VideoConferencev=newVideoConference(10); newThread(v).start(); //参与人员陆续进场 for(inti=0;i<10;i++){ Participantp=newParticipant(i+"号人员",v); newThread(p).start(); } } } classVideoConferenceimplementsRunnable{ privateCountDownLatchcontroller; publicVideoConference(intnum){ controller=newCountDownLatch(num); } publicvoidarrive(Stringname){ System.out.printf("%s已经到达!\n",name); controller.countDown(); System.out.printf("还需要等%d个成员!\n",controller.getCount()); } @Override publicvoidrun(){ try{ System.out.printf("会议正在初始化...!\n"); controller.await(); System.out.printf("所有人都到齐了,开会吧!\n"); } catch(InterruptedExceptione){ e.printStackTrace(); } } } classParticipantimplementsRunnable{ privateVideoConferenceconference; privateStringname; publicParticipant(Stringname,VideoConferenceconference){ this.name=name; this.conference=conference; } @Override publicvoidrun(){ longduration=(long)(Math.random()*100); try{ Thread.sleep(duration); conference.arrive(this.name); } catch(InterruptedExceptione){ } } }输出:
会议正在初始化...!
0号人员已经到达!
还需要等9个成员!
1号人员已经到达!
还需要等8个成员!
9号人员已经到达!
还需要等7个成员!
4号人员已经到达!
还需要等6个成员!
8号人员已经到达!
还需要等5个成员!
5号人员已经到达!
还需要等4个成员!
6号人员已经到达!
还需要等3个成员!
3号人员已经到达!
还需要等2个成员!
7号人员已经到达!
还需要等1个成员!
2号人员已经到达!
还需要等0个成员!
所有人都到齐了,开会吧!3.Phaser
importjava.util.concurrent.Phaser; importjava.util.concurrent.TimeUnit; importjava.util.List; importjava.util.ArrayList; importjava.io.File; importjava.util.Date; publicclassProgram{ publicstaticvoidmain(String[]agrs){ Phaserphaser=newPhaser(3); FileSearchsystem=newFileSearch("C:\\Windows","log",phaser); FileSearchapps=newFileSearch("C:\\ProgramFiles","log",phaser); FileSearchdocuments=newFileSearch("C:\\DocumentsAndSettings","log",phaser); ThreadsystemThread=newThread(system,"System"); systemThread.start(); ThreadappsThread=newThread(apps,"Apps"); appsThread.start(); ThreaddocumentsThread=newThread(documents,"Documents"); documentsThread.start(); try{ systemThread.join(); appsThread.join(); documentsThread.join(); }catch(InterruptedExceptione){ e.printStackTrace(); } System.out.println("Terminated:"+phaser.isTerminated()); } } classFileSearchimplementsRunnable{ privateStringinitPath; privateStringend; privateListresults; privatePhaserphaser; publicFileSearch(StringinitPath,Stringend,Phaserphaser){ this.initPath=initPath; this.end=end; this.results=newArrayList (); this.phaser=phaser; } privatevoiddirectoryProcess(Filefile){ File[]files=file.listFiles(); if(files!=null){ for(inti=0;i newResults=newArrayList (); longactualDate=newDate().getTime(); for(inti=0;i 运行结果:
Apps:Phase0:4results.
System:Phase0:27results.更多java相关内容感兴趣的读者可查看本站专题:《Java进程与线程操作技巧总结》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。