java創(chuàng)建子線程為什么要有兩種方法?

java創(chuàng)建子線程為什么要有兩種方法?
馬克-to-win:通過以下兩種方法創(chuàng)建子線程:1)聲明一個Thread類的子類。 2)實現(xiàn)runnable接口。java的官方文檔也沒強調這二者有什么區(qū)別。馬克-to-win:筆者認為,既然java只允許繼承一個類,如果你這個類本身就是某個類的子類,那你要想創(chuàng)建子線程,你就只能實現(xiàn)runnable這個接口。
馬克- to-win:馬克 java社區(qū):防盜版實名手機尾號: 73203。



例:1.3.2-本章源碼
class ThreadMark_to_win extends Thread {
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子線程i = " + i);
        }
    }
}
public class Test {
    public static void main(String[] args)  {
        Thread t = new ThreadMark_to_win();
        t.start();
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主線程i = " + i);
        }
    }
}
輸出結果:
主線程i = 0
子線程i = 0
主線程i = 1
子線程i = 1
主線程i = 2
子線程i = 2

馬克-to-win:對于以上例子的解釋:1)根據(jù)語法:在start以后,子線程被啟動,run方法被運行,與此同時cpu繼續(xù)執(zhí)行主線程的for循環(huán),這就是多線程的奧妙之處,所以當我們看結果時, 我們看到主線程和子線程交替輸出,如果子線程被擋住了,主線程是不受干擾的,這也是多線程的奧妙之處。馬克-to-win:在程序中,如果我不讓它睡覺停頓一秒鐘。CPU來不及切換就執(zhí)行完了,大家看不出效果。大家還以為是順序執(zhí)行的呢!其實是主子線程交替執(zhí)行的。2)馬克-to-win:當我的ThreadMark_to_win繼承了 Thread以后,為什么一start,run 就被運行,我們可以猜一猜sun公司是怎么實現(xiàn)的這一切?其實這就用到了我們前面學過的多態(tài),當父類指針指向子類時,當子類 ThreadMark_to_win沒有start方法時, 就執(zhí)行父類Thread的start方法, Thread的start方法說,執(zhí)行子類run方法,(我們的ThreadMark_to_win正好編了run方法)而且繼續(xù)主線程的下一句,于是就是我們前面看到的并發(fā)效果。

馬克-to-win:更有甚者,我們觀察下面的兩個例子會發(fā)現(xiàn):甚至當子線程拋出異常,子線程戛然停止崩潰時,主線程都不會受影響繼續(xù)運行,整個程序也照常運行,不會崩潰。反之亦然,主線程戛然停止崩潰時,整個程序不受影響,子程序照常運行。

為什么拋出異常,別的線程還是能運行呢?想象一下程序執(zhí)行的過程就明白了。jvm一句一句的向下執(zhí)行,當它發(fā)現(xiàn)異常時,它對別的線程,還是該干什么干什么。注意現(xiàn)在是你的程序出現(xiàn)問題,jvm一點問題都沒有,所以它還能正常該干什么干什么。(見下面的例子)




例:1.3.2_1-本章源碼
class ThreadMark_to_win extends Thread {
    public void run() {
        for (int i = 0; i < 7; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子線程i = " + i);
        }
    }
}
public class Test {
    public static void main(String[] args)  {
        Thread t = new ThreadMark_to_win();
        t.start();
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主線程i = " + i);
        }
        throw new ArithmeticException("divide by 0");
//后面不能再加任何語句了,會報unreachable compile error的
    }
}
輸出結果:
主線程i = 0
子線程i = 0
主線程i = 1
子線程i = 1
子線程i = 2
主線程i = 2Exception in thread "main"
java.lang.ArithmeticException: divide by 0
    at Test.main(Test.java:25)
子線程i = 3
子線程i = 4
子線程i = 5
子線程i = 6




例:1.3.2_2-本章源碼
class ThreadMark_to_win extends Thread {
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子線程i = " + i);
        }
        throw new ArithmeticException("divide by 0");
    }
}
public class Test {
    public static void main(String[] args)  {
        Thread t = new ThreadMark_to_win();
        t.start();
        for (int i = 0; i < 6; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主線程i = " + i);
        }
    }
}

輸出結果:
子線程i = 0
主線程i = 0
子線程i = 1
主線程i = 1
子線程i = 2
Exception in thread "Thread-0" java.lang.ArithmeticException: divide by 0
    at ThreadMark_to_win.run(Test.java:11)
主線程i = 2
主線程i = 3
主線程i = 4
主線程i = 5


下面介紹第二種方法創(chuàng)建子線程。





例:1.3.3-本章源碼

class CommonClassMark_to_win implements Runnable {
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子線程i = " + i);
        }
    }
}
public class Test {
    public static void main(String[] args)  {
        CommonClassMark_to_win cc = new CommonClassMark_to_win();
        Thread t=new Thread(cc);
        t.start();
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主線程i = " + i);
        }
    }
}
輸出結果:
主線程i = 0
子線程i = 0
子線程i = 1
主線程i = 1
主線程i = 2
子線程i = 2

后續(xù):
馬克-to-win:剛這個例子的CommonClassMark_to_win只是個普通的類,并沒有start方法,所以只能靠著Thread t=new Thread(cc);和線程扯上關系。輸出結果和第一種啟動線程的方法是一樣的。