这道面试题的内容是,要求两个线程交替打印,打印出"12A34B56C78D910E1112F1314G1516H1718I1920J2122K2324L2526M2728N2930O3132P3334Q3536R3738S3940T4142U4344V4546W4748X4950Y5152Z"。
一个线程只打印数字,另一个线程只打印字母,打印数字的线程打印两个数字后,打印字母的线程打印一个字母,然后交替下去直到打印完所有字母,数字打印到52即可。
这个的思路就是线程交替运行,交替运行的关键在于,一个线程执行完一个周期,立即挂起,同时通知另一个线程执行,另一个线程执行完,同样立即挂起,再通知之前的线程。两个线程都需要挂起和获得通知,但是两个线程是互相独立的,所以用对象监视器锁是不合适的,因为对象监视器的挂起和通知是无差异的,有可能会在挂起后将本线程立即激活,而需要被激活的线程仍在被挂起。。。所以这里要用ReentrantLock和两个Condition,不多说了看代码。
//线程A负责打印数字
class ThreadA extends Thread{
//一个锁和两个Condition
private Lock lock;
private Condition c1;
private Condition c2;
//构造方法注入这些引用
public ThreadA(Lock lock,Condition c1,Condition c2){
this.lock=lock;
this.c1=c1;
this.c2=c2;
}
//线程开始
public void run(){
try {
lock.lock();//加锁
//循环52次
for(int i=1;i<=52;i++){
//i为奇数时可以打印,打印两次
if(i%2!=0){
System.out.print(i+""+(i+1));
c2.signal();//通知c2这个condition开始运行
}else{
c1.await();//i为偶数时c1挂起
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class ThreadB extends Thread{
private Lock lock;
private Condition c1;
private Condition c2;
public ThreadB(Lock lock,Condition c1,Condition c2){
this.lock=lock;
this.c1=c1;
this.c2=c2;
}
public void run(){
try {
lock.lock();
char c='A';//定义char变量作为打印变量
for(int i=0;i<51;i++){//循环51次,因为遍历26个字母并且每个字母之间插入两个数字的话,需要循环51次,我是调试出来的,最初我也不知道需要51次。。。这里并不把char作为循环变量因为涉及到的是奇数次循环时不打印,所以char如果作为循环变量会跳过奇数次循环,会丢失打印。
if(i%2==0){//偶数次循环时打印c并自增,通知c1这个condition可以运行。
System.out.print(c++);
c1.signal();
}else{//循环奇数次时,c2这个condition挂起。
c2.await();
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
这是两个线程类,看main方法。
public static void main(String[] args) throws InterruptedException {
Lock lock=new ReentrantLock();
Condition c1=lock.newCondition();
Condition c2=lock.newCondition();
ThreadA a=new ThreadA(lock,c1,c2);
ThreadB b=new ThreadB(lock,c1,c2);
a.start();
Thread.sleep(50);
b.start();
}
这个没什么难的,线程A先启动,然后线程B再启动。结果正确。
我写的不一定是最好的,肯定有更优的解法,欢迎大家指正。