Java中使用Thread代表線程類,所有的線程類都必須是Thread類或者其子類的實例。每個線程的作用就是完成一定的任務,即執行一段程序流。Java中使用線程執行體來表示這段程序流。
Java中線程的實現方式有如下三種:
1.繼承Thread類
public class Thread extends Object implements Runnable
定義Thread類的子類,並重寫Thread類的run()方法,創建子類對象(即線程對象),調用線程對象的start()方法來啟動該線程。eg:
-
public class ThreadDemo extends Thread {
-
-
private int i;
-
public void run(){
-
for(; i<100 ;i++){
-
System.out.println(getName() + " "+ i);
-
}
-
}
-
public static void main(String[] args) {
-
for(int i = 0 ;i<100; i++){
-
//currentThread是Thread類的靜態方法,該方法返回當前正在執行的線程對象
-
//getName()返回當前線程對象的名字
-
System.out.println(Thread.currentThread().getName()+ " "+i);
-
if(i==20){
-
//啟動兩個線程,但是實際上有三個線程,即main主線程
-
//用戶啟動的多個線程的名字依次為Thread-0、Thread-1、、、、
-
new ThreadDemo().start();
-
new ThreadDemo().start();
-
}
-
}
-
-
}
-
-
}
注意:該例中第一次出現的變量i是實例變量,而每次創建線程對象時候,Thread-0和Thread-1兩個線程對象不能共享實例變量i。即使用繼承Thread方法創建線程對象時,多個線程之間無法共享線程類的實例變量。
2.實現Runnable接口
public interface Runnable
定義Runnable接口的實現類,並重寫該接口的run()方法,該run()方法同樣是該線程的執行體。創建該Runnable實現類的實例,並將此實例作為Thread的target(即構造函數中的參數)來創建Thread對象(該Thread對象才是真正的線程對象,只是該Thread對象負責執行其target的run()方法)。最后調用線程對象的start()方法來啟動該線程。eg:
-
public class ThreadDemo implements Runnable {
-
-
private int i;
-
<strong> public void run</strong>(){
-
for(; i<100 ;i++){
-
//當線程類實現Runnable接口時,只能通過Thread.currentThread()方法獲得當前線程
-
System.out.println(Thread.currentThread().getName() + " "+ i);
-
}
-
}
-
public static void main(String[] args) {
-
for(int i = 0 ;i<100; i++){
-
System.out.println(Thread.currentThread().getName()+ " "+i);
-
if(i==20){
-
ThreadDemo td = new ThreadDemo();
-
//創建兩個Thread對象,並且均把Runnable接口實例對象作為target
-
new Thread(td).start();
-
new Thread(td).start();
-
}
-
}
-
-
}
-
-
}
從運行的結果可以看到實例變量i的輸出是連續的。也就是使用Runnable接口創建的多個線程是可以共享線程類的實例變量,這是因為多個線程可以共享同一個target,所以多個線程可以共享同一個線程類(target類)的實例屬性。
3.使用Callable和Future
public interface Future<V>
public interface Callable<V>
public interfaceRunnableFuture<V> extends Runnable, Future<V>
public class FutureTask<V>extends Object implementsRunnableFuture<V>
Java5之后,提供了Callable接口,看起來像是Runnable接口的增強版:該接口提供call()方法來作為線程執行體。與run()相比,call()方法更強大,該方法可以有返回值,並且可以聲明拋出異常。
但是Callable不是Runnable接口的子類,不能作為Thread的target。而且call()方法返回的值如何調用呢?
Java5給出了Future接口,來獲得call()方法的返回值。並提供了FutureTask實現類,該類實現了Runnale接口和Future接口---可以作為Thread類的target。
創建並啟動有返回值的線程的步驟如下:
創建Callable接口的實現類,並實現call()方法,該方法有返回值;創建Callable實現類的實例,使用FutureTask來包裝Callable對象,並且也封裝了call()方法的返回值;使用FutureTask作為Thread類的target創建並啟動線程;調用FutureTask對象的get()方法返回子線程執行結束后的返回值。
-
import java.util.concurrent.Callable;
-
import java.util.concurrent.FutureTask;
-
-
public class CallableDemo implements Callable<Integer>{
-
//實現call()方法,作為線程執行體
-
public Integer call(){
-
int i = 5;
-
for( ; i<100 ; i++){
-
System.out.println(Thread.currentThread().getName() + "的循環變量i的值:" +i);
-
}
-
//call()方法可以有返回值
-
return i;
-
}
-
public static void main(String[] args) {
-
//創建Callable對象
-
CallableDemo cd = new CallableDemo();
-
//使用FutureTask來包裝Callable對象
-
FutureTask<Integer> task = new FutureTask<Integer>(cd);
-
for(int i=0 ; i<100 ; i++){
-
System.out.println(Thread.currentThread().getName() + "的循環變量i的值:" +i);
-
if(i==20){
-
//實質還是以Callable對象來創建並啟動線程
-
new Thread(task,"有返回值的線程").start();
-
}
-
}
-
try{
-
System.out.println( "子線程的返回值" + task.get());
-
-
} catch(Exception e){
-
e.printStackTrace();
-
}
-
-
}
-
-
}
FutureTask是Future接口的實現類。Future接口提供了一些方法來控制他關聯的Callable任務。
boolean |
cancel(boolean mayInterruptIfRunning)
Attempts to cancel execution of this task. //視圖取消該Future關聯的Callable任務
|
V |
get()
Waits if necessary for the computation to complete, and then retrieves its result.
//返回call()方法的返回值,調用該方法將導致程序阻塞,必須等到子線程結束后才會有返回值 |
V |
get(long timeout, TimeUnit unit)
Waits if necessary for at most the given time for the computation to complete, and then retrieves its result, if available.
//設置阻塞時間 |
boolean |
isCancelled()
Returns
true if this task was cancelled before it completed normally.
|
boolean |
isDone()
Returns
true if this task completed.
|
Callable有泛型限制,Callable接口里的泛型形參類型與Call()方法返回值類型相同。
4.創建線程的三種方式對比
實現Runnable和Callable接口的方法基本相同,只不過Callable接口定義的方法可以有返回值,而且可以聲明拋出異常而已。
因此采用實現Runnable和Callable接口方式創建多線程——
優勢:
1.線程類只是實現了接口類,還可以繼承其他類
2.在這種方式下,多個線程可以共享同一個target對象,所以非常適合多個相同線程類處理同一份資源的情況。
劣勢:
編程稍稍復雜,如果要訪問當前線程必須使用Thread.currentThread()方法
而繼承Thread方式則與之相反,因為已經繼承了Thread類,不能再繼承其他類。編寫簡單,如果要訪問當前線程,直接使用this即可獲得當前線程。
故一般建議采用實現Runnable、Callable接口的方式實現多線程。
--------------------- 本文來自 亮仔亮仔我愛你喲 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/fffllllll/article/details/52269568?utm_source=copy