創建新執行線程有三種方法。
第一種方法是將類聲明為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。接下來可以分配并啟動該子類的實例。
例如,計算大于某一規定值的質數的線程可以寫成:
class PrimeThread extends Thread {long minPrime;PrimeThread(long minPrime) {this.minPrime = minPrime;}public void run() {// compute primes larger than minPrime. . .}}
然后,下列代碼會創建并啟動一個線程:
PrimeThread p = new PrimeThread(143);p.start();
1,繼承Thread
定義類繼承Thread
重寫run方法
把新線程要做的事寫到run方法中
創建線程對象
開啟新線程,內部會自動執行run方法
具體的多線程實現代碼如下:
package com.yy.thread;public class Demo2_Thread {
/*** 創建新執行線程有兩種方法。* 一種方法是* ①將類聲明為 Thread 的子類。* ②該子類應重寫 Thread 類的 run 方法。* ③接下來可以分配并啟動該子類的實例。* */public static void main(String[] args) {//開啟一條線程Mythread mt = new Mythread(); //4,創建線程(Thread類)的子類對象mt.start(); //5,開啟線程,需要一定的時間,所以說,JVM會先執行for循環語句,也就是,會先輸出bb,之后再輸出aaaaaaa;然后再相互交替輸出//public void start() : 使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
// mt.run(); //如果這樣調用,只是調用了run方法,執行run方法里面的for循環,線程還是主線程這一個線程,不會出現交替的多線程形式//主線程for (int i = 0; i < 100; i++) { //為了便于觀察多線程的執行,在主方法里面進行for循環System.out.println("bb"); //這個for循環就是主線程的,兩條線程開啟多線程便于觀察}//兩條線程相互交替進行,即多線程實現}}
class Mythread extends Thread{ //1,繼承Threadpublic void run(){ //2,重寫run方法for (int i = 0; i < 100; i++) { //3,將要執行的代碼寫到run方法中System.out.println("aaaaaaa"); }}
}
創建線程的第二種方法是聲明實現 Runnable 接口的類。該類然后實現 run 方法。
然后可以分配該類的實例,在創建 Thread 時作為一個參數來傳遞并啟動。
采用這種風格的同一個例子如下所示:
class PrimeRun implements Runnable {long minPrime;PrimeRun(long minPrime) {this.minPrime = minPrime;}public void run() {// compute primes larger than minPrime. . .}}
然后,下列代碼會創建并啟動一個線程:
每個線程都有一個標識名,多個線程可以同名。如果線程創建時沒有指定標識名,就會為其生成一個新名稱。
PrimeRun p = new PrimeRun(143);new Thread(p).start();
2,實現Runnable
定義類實現Runnable接口
實現run方法
把新線程要做的事先到run方法中
創建自定義的Runnable子類對象
創建Tread對象,傳入Runnable
調用start()開啟新線程,內部會自動調用Runnable的run()方法
具體的多線程實現代碼如下:
package com.yy.thread;public class Demo3_Thread {
/*** 創建線程的另一種方法是* ①聲明實現 Runnable 接口的類。* ②該類然后實現 run 方法。* ③然后可以分配該類的實例,在創建 Thread 時作為一個參數來傳遞并啟動。* * Runnable里面只有一個run()方法,實現Runnable只需要重寫run()方法即可* * * 實現Runnable的原理* 查看源碼:* 1,看Thread類的構造函數,傳遞了Runnable接口的引用* 2,通過init()方法找到傳遞的target給成員變量的target賦值* 3,查看run方法,發現run方法中有判斷,如果target不為null就會調用Runnable接口子類對象的run方法* */public static void main(String[] args) {MyRunnable wsq = new MyRunnable(); //4,創建Runnable的子類對象
// Runnable target = wsq; //父類引用指向子類對象,new MyRunnable()這個是子類對象也就是wsq Thread yy = new Thread(wsq); //5,將其當作參數傳遞給Thread的構造函數; wsq代表Runnable的子類對象yy.start(); //6,開啟線程; Thread里面才有start()方法// new Thread(wsq).start(); //這種方法與上面的兩行代碼實現的做用一樣for (int i = 0; i < 100; i++) {System.out.println("你,陪我步入蟬夏,越過城市喧囂,歌聲還在游走,你榴花般的笑容");}}
}
class MyRunnable implements Runnable { //1,定義一個類,實現Runnable方法@Override public void run() { //2,重寫run方法for (int i = 0; i < 100; i++) { //3,將要執行的代碼寫到run方法中System.out.println("我真的好想你,在每一個雨季。"); }}
}
兩種方法的區別:
- a.繼承Thread : 由于子類重寫了Thread類的run(),當調用start()時,直接找子類的run()方法
- b.實現Runnable : 構造函數中傳入了Runnable的引用,成員變量記住了它,start()調用run()方法時內部判斷成員變量Runnable的引用是否為空,不為空,編譯時看的是Runnable的run(),運行時執行的時子類的run()方法
繼承Thread(開發中優先考慮)
好處:可以直接使用Thread類中的方法,代碼簡單
弊端:如果已經有了父類,就不能有這個方法,因為java是單繼承的
實現Runnable接口(實際上是對繼承Thread的補充)
好處:即使自己定義的線程類有了父類也沒關系,因為有了父類也可以實現接口,接口是可以多實現的,拓展性強
弊端:不能直接使用Thread中的方法,需要先獲取到線程對象后,才能得到Thread的方法,代碼復雜
第三種方法是創建類是實現Callable這個接口。然后該類重寫 Callable這個接口里的抽象方法call。
Callable 接口類似于 Runnable,兩者都是為那些其實例可能被另一個線程執行的類設計的。但是 Runnable 不會返回結果,并且無法拋出經過檢查的異常。
案例:通過實現Callable這個接口,來實現多線程,創建一個求和類;
具體的多線程實現代碼如下:
package com.yy.thread;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class Demo6_Callable {
/*** 第三種線程創建方式* <T> Future<T> submit(Callable<T> task)* 提交一個返回值的任務用于執行,返回一個表示任務的未決結果的 Future。* 該 Future 的 get 方法在成功完成時將會返回該任務的結果。 * Callable 接口類似于 Runnable,兩者都是為那些其實例可能被另一個線程執行的類設計的。但是 Runnable 不會返回結果,并且無法拋出經過檢查的異常。* * */public static void main(String[] args) throws InterruptedException, ExecutionException {ExecutorService pool = Executors.newFixedThreadPool(2); //創建線程池,線程池里面可以養兩個線程Future<Integer> f1 = pool.submit(new MyCallable(100)); //由于下面創建的類中里面有一個有參構造方法,這里得傳一個參數Future<Integer> f2 = pool.submit(new MyCallable(50));//submit返回一個Future,所以說,應該用Future去接收//Future可以獲取出來1-100或者是1-50的求和結果System.out.println(f1.get());System.out.println(f2.get());//V get() throws InterruptedException,ExecutionException如有必要,等待計算完成,然后獲取其結果。 pool.shutdown(); //關閉線程池}}
class MyCallable implements Callable<Integer>{ //創建一個MyCallable類去實現Callable這個接口private int num; //設置一個成員變量numpublic MyCallable (int num){ //創建一個有參構造方法this.num = num; //num是傳進去的數}@Overridepublic Integer call() throws Exception { //重寫Callable這個接口里面的抽象方法call;這里的返回值類跟泛型<Integer>是一致的int sum = 0; //實現求和代碼for (int i = 1; i <= num; i++) {sum += i;}return sum; //返回sum}}