Java多線程中Thread與Runnable的區別
定義
extends Thread
子類繼承Thread具備多線程能力,可以實現多線程;啟動線程的方法:①創建子類對象 ②對象名.start();不建議使用:避免多線程OOP單繼承的局限性(OOP:Object Oriented Programming,面向對象的編程、類似的還有OOD(面向對象的設計),OOA(面向對象的分析));implements Runnable
實現Runnable接口具有多線程能力,可以實現多線程啟動線程的方法:①創建子類對象 ②new Thread(對象).strart();推薦使用:避免OOP單繼承的局限性,方便被同一個對象多次使用;實際上所有的多線程代碼都是通過執行Thread的start()方法來運行的。所以不管是繼承Thread類還是實現Runnable接口來實現多線程,最終都是通過Thread對象的API來控制線程的,因此熟悉Thread類的API是進行多線程編程的基礎。(點擊查看什麼是API)
示例
繼承Thread實現多線程1
package 多線程1;
public class TestThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("子線程正在執行..."+i);
}
}
public static void main(String[] args) {
TestThread1 testThread1 = new TestThread1();
testThread1.start();
for (int i = 0; i < 500; i++) {
System.out.println("主線程正在執行..."+i);
}
}
}
上述示例執行後可以看到:主線程和子線程的執行順序本該是「先執行子線程、再執行主線程」,事實上卻是主線程和子線程交替執行(無法人為控制,由CPU調度執行),這便是多線程的特徵;
繼承Thread實現多線程2——下載網絡圖片
package 多線程1;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
//練習網絡下載圖片
public class TestThread2 extends Thread {
private String url;
private String name;
public TestThread2(String url,String name){
this.name=name;
this.url=url;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.Downloader(url,name);
System.out.println("下載的文件名為 "+name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("雙引號內填網絡圖片url","p1.jpg");
TestThread2 t2 = new TestThread2("雙引號內填網絡圖片url","p2.jpg");
TestThread2 t3 = new TestThread2("雙引號內填網絡圖片url","p3.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下載器
class WebDownloader{
public void Downloader(String url,String name) {
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO錯誤!");
}
}
}
註:代碼中圖片為網絡上隨機尋找
由以上代碼也可以看出:下載的三張圖片的順序本應該是"p1-->p2-->p3",事實上執行後發現三張圖片的下載順序是隨機的(受CPU自由調度)。
Runnable接口實現多線程1
package 多線程1;
public class TestThread4_Runnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我是小兔子"+i);
}
}
public static void main(String[] args) {
TestThread4_Runnable testThread4_runnable = new TestThread4_Runnable();
Thread thread = new Thread(testThread4_runnable);
thread.start();
for (int i = 0; i < 800; i++) {
System.out.println("但我是一隻小腦斧哦~"+i);
}
}
}
上述代碼的執行結果也是隨機的,無法根據代碼的編寫順序來判斷執行順序。
Runnable接口實現多線程2——龜兔賽跑
package 多線程1;
public class Race implements Runnable {
private String winner=null;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(Thread.currentThread().getName().equals("兔子") && i%5==0) { //模擬兔子休息(1ms)
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boolean winner = Win(i);
if(winner)
break;
System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
}
}
public boolean Win(int steps){ //判斷比賽是否結束
if(winner!=null)
return true;
else if(steps==100) {
winner = Thread.currentThread().getName();
System.out.println(winner + "已經完成比賽!");
return true;
}
else
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"烏龜").start();
}
}
上述代碼模擬了龜兔賽跑,烏龜和兔子「隨機跑步」,兔子在中途「頻繁」休息,執行結果最終是烏龜成為了winner!
Thread.sleep()方法調用的目的是不讓當前線程獨自霸佔CPU,以留出一定時間給其他線程執行的機會。 實際上所有的多線程代碼執行順序都是不確定的,每次執行的結果都是隨機的。
但由於CPU的執行速度非常快,在測試的數量比較少的情況下可能無法看出隨機執行的現象!