Java多线程连续打印abc实现方法详解
一道编程题如下:
实例化三个线程,一个线程打印a,一个线程打印b,一个线程打印c,三个线程同时执行,要求打印出10个连着的abc。
题目分析:
通过题意我们可以得出,本题需要我们使用三个线程,三个线程分别会打印6次字符,关键是如何保证顺序一定是abc...呢。所以此题需要同步机制来解决问题!
令打印字符A的线程为ThreadA,打印B的ThreadB,打印C的为ThreadC。问题为三线程间的同步唤醒操作,主要的目的就是使程序按ThreadA->ThreadB->ThreadC->ThreadA循环执行三个线程,因此本人整理出了三种方式来解决此问题。
一、通过两个锁(不推荐,可读性和安全性比较差)
packagecom.demo.test;
/**
*基于两个lock实现连续打印abcabc....
*@authorlixiaoxi
*
*/
publicclassTwoLockPrinterimplementsRunnable{
//打印次数
privatestaticfinalintPRINT_COUNT=10;
//前一个线程的打印锁
privatefinalObjectfontLock;
//本线程的打印锁
privatefinalObjectthisLock;
//打印字符
privatefinalcharprintChar;
publicTwoLockPrinter(ObjectfontLock,ObjectthisLock,charprintChar){
this.fontLock=fontLock;
this.thisLock=thisLock;
this.printChar=printChar;
}
@Override
publicvoidrun(){
//连续打印PRINT_COUNT次
for(inti=0;i
打印结果:
ABCABCABCABCABCABCABCABCABCABC
分析:
此解法为了为了确定唤醒、等待的顺序,每一个线程必须同时持有两个对象锁,才能继续执行。一个对象锁是fontLock,就是前一个线程所持有的对象锁,还有一个就是自身对象锁thisLock。主要的思想就是,为了控制执行的顺序,必须要先持有fontLock锁,也就是前一个线程要释放掉前一个线程自身的对象锁,当前线程再去申请自身对象锁,两者兼备时打印,之后首先调用thisLock.notify()释放自身对象锁,唤醒下一个等待线程,再调用fontLock.wait()释放prev对象锁,暂停当前线程,等待再次被唤醒后进入循环。运行上述代码,可以发现三个线程循环打印ABC,共10次。程序运行的主要过程就是A线程最先运行,持有C,A对象锁,后释放A锁,唤醒B。线程B等待A锁,再申请B锁,后打印B,再释放B锁,唤醒C,线程C等待B锁,再申请C锁,后打印C,再释放C锁,唤醒A。看起来似乎没什么问题,但如果你仔细想一下,就会发现有问题,就是初始条件,三个线程按照A,B,C的顺序来启动,按照前面的思考,A唤醒B,B唤醒C,C再唤醒A。但是这种假设依赖于JVM中线程调度、执行的顺序,所以需要手动控制他们三个的启动顺序,即Thread.Sleep(100)。
二、通过一个ReentrantLock和三个conditon实现(推荐,安全性,性能和可读性较高)
packagecom.demo.test;
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.ReentrantLock;
/**
*基于一个ReentrantLock和三个conditon实现连续打印abcabc...
*@authorlixiaoxi
*
*/
publicclassRcSyncPrinterimplementsRunnable{
//打印次数
privatestaticfinalintPRINT_COUNT=10;
//打印锁
privatefinalReentrantLockreentrantLock;
//本线程打印所需的condition
privatefinalConditionthisCondtion;
//下一个线程打印所需要的condition
privatefinalConditionnextCondtion;
//打印字符
privatefinalcharprintChar;
publicRcSyncPrinter(ReentrantLockreentrantLock,ConditionthisCondtion,ConditionnextCondition,
charprintChar){
this.reentrantLock=reentrantLock;
this.nextCondtion=nextCondition;
this.thisCondtion=thisCondtion;
this.printChar=printChar;
}
@Override
publicvoidrun(){
//获取打印锁进入临界区
reentrantLock.lock();
try{
//连续打印PRINT_COUNT次
for(inti=0;i
打印结果:
ABCABCABCABCABCABCABCABCABCABC
分析:
仔细想想本问题,既然同一时刻只能有一个线程打印字符,那我们为什么不使用一个同步锁ReentrantLock?线程之间的唤醒操作可以通过Condition实现,且Condition可以有多个,每个condition.await阻塞只能通过该condition的signal/signalall来唤醒!这是synchronized关键字所达不到的,那我们就可以给每个打印线程一个自身的condition和下一个线程的condition,每次打印字符后,调用下一个线程的condition.signal来唤醒下一个线程,然后自身再通过自己的condition.await来释放锁并等待唤醒。
三、通过一个锁和一个状态变量来实现(推荐)
packagecom.demo.test;
/**
*基于一个锁和一个状态变量实现连续打印abcabc...
*@authorlixiaoxi
*
*/
publicclassStateLockPrinter{
//状态变量
privatevolatileintstate=0;
//打印线程
privateclassPrinterimplementsRunnable{
//打印次数
privatestaticfinalintPRINT_COUNT=10;
//打印锁
privatefinalObjectprintLock;
//打印标志位和state变量相关
privatefinalintprintFlag;
//后继线程的线程的打印标志位,state变量相关
privatefinalintnextPrintFlag;
//该线程的打印字符
privatefinalcharprintChar;
publicPrinter(ObjectprintLock,intprintFlag,intnextPrintFlag,charprintChar){
super();
this.printLock=printLock;
this.printFlag=printFlag;
this.nextPrintFlag=nextPrintFlag;
this.printChar=printChar;
}
@Override
publicvoidrun(){
//获取打印锁进入临界区
synchronized(printLock){
//连续打印PRINT_COUNT次
for(inti=0;i
打印结果:
ABCABCABCABCABCABCABCABCABCABC
分析:
状态变量是一个volatile的整型变量,0代表打印a,1代表打印b,2代表打印c,三个线程都循环检验标志位,通过阻塞前和阻塞后两次判断可以确保当前打印的正确顺序,随后线程打印字符,然后设置下一个状态字符,唤醒其它线程,然后重新进入循环。
补充题
三个Java多线程循环打印递增的数字,每个线程打印5个数值,打印周期1-75,同样的解法:
packagecom.demo.test;
importjava.util.concurrent.atomic.AtomicInteger;
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.ReentrantLock;
/**
*数字打印,三个线程同时打印数字,第一个线程打印12345,第二个线程打印678910.........
*@authorlixiaoxi
*
*/
publicclassNumberPrinter{
//打印计数器
privatefinalAtomicIntegercounter=newAtomicInteger(0);
privateclassPrinterimplementsRunnable{
//总共需要打印TOTAL_PRINT_COUNT次
privatestaticfinalintTOTAL_PRINT_COUNT=5;
//每次打印PER_PRINT_COUNT次
privatestaticfinalintPER_PRINT_COUNT=5;
//打印锁
privatefinalReentrantLockreentrantLock;
//前一个线程的condition
privatefinalConditionafterCondition;
//本线程的condition
privatefinalConditionthisCondtion;
publicPrinter(ReentrantLockreentrantLock,ConditionthisCondtion,ConditionafterCondition){
super();
this.reentrantLock=reentrantLock;
this.afterCondition=afterCondition;
this.thisCondtion=thisCondtion;
}
@Override
publicvoidrun(){
//进入临界区
reentrantLock.lock();
try{
//循环打印TOTAL_PRINT_COUNT次
for(inti=0;i
运行结果:
2
4
6
8
10
12
14
16
18
20
22
24
26
28
30
32
34
36
38
40
42
44
46
48
50
52
54
56
58
60
62
64
66
68
70
72
74
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。