這道面試題的內容是,要求兩個線程交替列印,列印出"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再啟動。結果正確。
我寫的不一定是最好的,肯定有更優的解法,歡迎大家指正。