一道基礎的多線程題目

  以前把thread基礎溫習了一遍,而後想經過作一些題目來加深本身的印象,恰好大四找工做面試時被問到一道題目:A-Z,1-26,使用多線程打印出A1B2C3D4E5......這種,當時本身好像回答的並很差,因而今天從新寫了一遍。面試

  首先咱們須要新建兩個類,一個用來打印A-Z,一個用來打印1-26。這裏爲了省略,我只打印了前面幾個數字和字母。咱們一步一步來。多線程

public class PrintCharacter extends Thread {


    @Override
    public void run() {
        List<String> list = Arrays.asList("A", "B", "C", "D", "E");
        for (String str : list) {
            System.out.println(str);
        }
    }
}
public class PrintNumber extends Thread {

    @Override
    public void run() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        for (Integer i : list) {
            System.out.println(i);
        }
    }
}
public static void main(String[] args) {
        PrintCharacter pc = new PrintCharacter();
        PrintNumber pn = new PrintNumber();
        pc.setPriority(10);
        pn.setPriority(1);
        pc.start();
        pn.start();
    }

  這裏的代碼比較簡單,主線程開啓了兩個線程,並設置打印字母的線程的優先級爲最高(網上說設置優先級並不能必定保證會先執行,可是我這裏試驗過不少次都是先打印字母的)。經過代碼咱們看出不出意外結果應該是ABCDE12345這樣打印的。不符合題目。而後我想到了sleep()這個方法,他可讓線程休眠必定時間再進入就緒狀態,那麼理論上他是徹底能夠實現題目的效果的。ide

public class PrintCharacter extends Thread {


    @Override
    public void run() {
        List<String> list = Arrays.asList("A", "B", "C", "D", "E");
        for (String str : list) {
            try {
                System.out.println(str);
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class PrintNumber extends Thread {

    @Override
    public void run() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        for (Integer i : list) {
            try {
                System.out.println(i);
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

  這裏與上面代碼的區別就是每次循環打印以後我讓線程休眠0.5s,首先打印字母A和數字1,而後兩個線程休眠0.5s,而後繼續打印,直到循環結束。運行結果證實這種方法的確可行,可是這種方法只能說完成了題目要求的。他須要線程一直休眠。這種實現方法我的以爲能夠幫助咱們理解sleep()方法,可是並很差。這個時候我想到了另外的一種實現方法,那就是多線程中最經常使用的鎖,經過鎖來實現交替打印。在說這種方法以前咱們再次說明wait()方法和notify()方法。this

  wait()方法和notify()方法的使用前提都須要拿到鎖,所以他們一般在同步代碼塊中使用。wait()方法會釋放當前線程的鎖,而後自身進入阻塞狀態。notify()方法會喚醒一個正在阻塞狀態的線程,而後他不會當即釋放鎖,而是等到執行完同步代碼塊中的內容,在釋放鎖。而後就是wait()方法和notify()方法的順序不能錯,即先執行wait()方法在執行notify()方法才能被喚醒。介紹完這兩個方法,而後咱們開始看代碼。spa

public class PrintCharacter extends Thread {

    private Object lock;

    public PrintCharacter(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        List<String> list = Arrays.asList("A", "B", "C", "D", "E");
        for (String str : list) {
            synchronized (lock) {
                try {
                    lock.notify();
                    System.out.println(str);
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
public class PrintNumber extends Thread {

    private Object lock;

    public PrintNumber(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        for (Integer i : list) {
            synchronized (lock) {
                try {
                    lock.notify();
                    System.out.println(i);
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
    public static void main(String[] args) {
        Object lock = 0;
        PrintCharacter pc = new PrintCharacter(lock);
        PrintNumber pn = new PrintNumber(lock);
        pc.setPriority(10);
        pn.setPriority(1);
        pc.start();
        pn.start();
    }

  首先,咱們分析一下代碼,這兩個線程都有一個lock對象,並且經過synchronized關鍵字保證這個對象不能同時被多個線程訪問。而後在run()方法中,首先喚醒一個正在阻塞狀態的線程,而後打印。而後讓釋放鎖並讓自身阻塞。主線程運行時執行兩個子線程,首先執行打印字母的線程,他執行notify()方法,由於當前沒有線程處於阻塞狀態,因此不起做用,而後打印A,以後釋放鎖,並讓自身阻塞。而後打印數字的線程得到鎖開始執行,他執行notify()方法喚醒打印字母的線程,而後打印1,而後讓自身阻塞,最後釋放鎖。這樣一直循環。直到循環結束。線程

  這種方法看上去挺好的,可是實際上仍是有問題的,問題就是當兩個線程最後一次循環的時候打印數字線程會調用wait()方法令自身阻塞。而後致使程序沒法結束,,,這樣看起來問題好像更嚴重了。。。既然打印數字得線程最後會阻塞,那麼咱們將它得wait()方法改成wait(1000),這樣當最後一次它阻塞自身1s後,就會從新就如就緒狀態執行完run()方法。這樣程序就能正常退出了。code

相關文章
相關標籤/搜索