java 多線程淺析

1. 線程與進程之間的區別與聯繫
    在操作系統的資源管理中,某個時間內只能有一個進程佔用時間,
    所謂線程就是在進程的基礎上進一步的劃分得來的,一個進程可以有多個線程,
    線程是比進程更快的處理單元,而且佔用的資源比較少。
    多線程的啓動,並不是按照順訊一個一個的執行,而是誰能夠佔有資源誰就先執行。

    個人理解看來,一個進程就像是一個圖片下載器,
    在網速足夠的情況下,如果這個軟件只有一個線程,
    那麼在一個時間內,只能通過這個線程下載一幅圖像,
    而如果這個下載器是多線程下載器,那麼在同一時間內,
    就可以通過多個線程下載多幅圖像。但同時資源佔有率也比較高。
    總的來說,線程的實現離不開進程,就比如這個軟件,如果軟件關閉了,那麼進程也就沒了。
    
2. 多線程的實現方式一共有三種:
    第一種是繼承 Thread 類,Thread 是一個多線程的功能類,所有繼承 Thread 的類都是多線程類。
    第二種是實現 Runnable 接口。
    第三種是從java 1.5 以後增加的實現 Callable 接口。
    這兩種接口的區別在於,Runnable 沒有返回值,但是Callable 接口具有返回值。
    
   (1)類繼承 Thread :
       class MyMessage extends Thread {}  
       MyMessage 由於繼承了 Thread,所以這就是一個多線程類。 
       每一個程序的的起點都是main方法,但是每一個線程也有起點,
       這個起點就是 run() 方法,每一個繼承了 Thread 的多線程子類都需要覆寫 run() 方法。
       
       Thread 類中的 run 方法: public void run(){}; 

    類繼承 Thread 多線程舉例:

    
      
   
class ThreadTest extends Thread{  線程主類, 通過繼承 Thread 實現 多線程
	private String name;
	public ThreadTest(String name) {
		this.name = name;
	}
	@Override
	public void run() {   覆寫 run() 方法
		for(int i = 0; i < 100; i++) {
			System.out.println(this.name + "-->" + i);
		}
	} 
	
    }

public class ThreadDemo {   主類
	public static void main(String[] args) {

		ThreadTest thread1 = new ThreadTest("繼承Thread多線程1");
		ThreadTest thread2 = new ThreadTest("繼承Thread多線程2");
		ThreadTest thread3 = new ThreadTest("繼承Thread多線程3");
  
		thread1.start();  
		thread2.start();
		thread3.start();

        通過調用 run(); 也可以啓動程勳,但是都是順序知執行, 不會出現交替執行的現象。
        所以只能通過調用 Thread 類中的 start(); 方法來啓動多線程。
        通過start(); 方法 可以啓動多線程,每一個線程對象交替執行, 使用start(); 
        進行啓動多線程,因爲其不僅僅是啓動線程,還會根據不同的操作系統進行資源的分配。
        調用 Thread 父類中的 start(); 啓動多線程 , 但是啓動的內容爲 run(); 的方法體
	}
}
    之所以調用 start() 可以實現多線程的原因在於,
    start() 方法中有一個 JNI (java Native Interface) 方法
    這個方法可以實現對特定的操作系統進行資源的分配。
    但是由於是對特定的系統,所以在一定程度上喪失了可移植性。
    所以 調用 start() 方法,不僅僅是像 run() 方法一樣只是執行了多線程代碼
    還實現了對不同操作系統對線程進行資源的分配。


(2) 用 Runnable 實現多線程
      雖然採用繼承 Thread 類的方法可以實現多線程
      但是卻又一個缺點,由於 java 的單繼承模式,如果這個類
      繼承了 Thread 類,那麼久無法再繼承別的類了,所以爲了避免
      這個問題的出現,也可以用實現 Runnable 接口的方法來實現多線程。
    
    實現 Runnable 接口多線程舉例:
        
    class ThreadImpel implements Runnable {  

        線程主類, 通過 實現 Runnable 接口 實現多線程, 
        並且也需要實現 Runnable 實現 run()方法,但是並沒有 start() 方法

	    private String name;
	    public ThreadImpel(String name) {
		    this.name = name;
	    }
	    public String getname() {
		    return this.name;
	    }
	    @Override
	    public void run() {  // 覆寫 run() 方法
		    for(int i = 0; i < 100; i++) {
			    System.out.println(this.name + "-->" + i);
		    }
	    } 	    
    }
    
    實際上,Thread 類中的 run() 方法也是實現了 Runnable 的接口。
    但是雖然實現 Runnable 接口實現了多線程,但是這個接口中由於缺少了
    start() 方法,所以不能啓動多線程。

    但是在 Thread 的類中,存在一個構造方法:
    public Thread (Runnable Target) {};
    這個構造方法可以接受一個 Runnable 接口實現類的對象,並返回一個 Thread 對象
    
    所以在 main 方法中可以這麼寫:

        public class ThreadDemo {     主類
	        public static void main(String[] args) {
		    
		    ThreadImpel thread1 = new ThreadImpel("實現接口Runnable多線程1");
		    ThreadImpel thread2 = new ThreadImpel("實現接口Runnable多線程2");
		    ThreadImpel thread3 = new ThreadImpel("實現接口Runnable多線程3");

		    new Thread(thread1).start();
		    new Thread(thread2).start();
		    new Thread(thread3).start();
            
            或者寫成:
            Thread th1 = new Thread(thread1);
            Thread th2 = new Thread(thread1);
            Thread th3 = new Thread(thread1);
            th1.start();  啓動多線程
            th2.start();
            th3.start();
    	
	        }
       } 

  實現 Runnable 接口與繼承 Thread 的區別, 由於 java 是單繼承模式,所以實現 Runnable 接口解決了這個侷限性。

Thread 類的定義:  public class Thread extens Object implements Runnable

從定義上可以看出,實際上Thread類中的 run() 方法也是實現與 Runnable 接口。

還有第三種實現多線程的方法就是 類實現 Callable 接口,這個接口個 Runnable 接口的不同之處就是在於,Callable接口可以有返回值。返回值爲泛型,在實現接口的實現定義返回值類型。

(3)
    通過實現 Callable 接口實現多線程
    但是類聲明完成之後由於不是 Thread 的子類,並且
    Thread 類的構造方法中也不含有接收 Callable 接口對象的方法
    所以這個時候要依靠
 
     類:FutureTask   
       public class FutureTask<V> implements RunnableFuture<V> {
           public FutureTask(Callable<V> callable){  構造方法
            }
        }
     接口:RunnableFuture
       public interface RunnableFuture<V> extends Runnable, Future<V> {}
     
    由這個接口和類可以看出,FutureTask 的構造方法接收 Callable 接口對象,又
    由於這個類實現了 RunnableFuture 這個接口。而這個接口又繼承 Runnable 接口。
    所以就可以使用 Thread 中的構造方法來實現多線程的啓動。
    
多線程實現類:

    class ThreadCall implements Callable <String > {
	    private int ticket = 20;
	
	    @Override
	    public String call() throws Exception {
		    for(int i = 0; i < 100; i++) {
			    if(this.ticket > 0) {
				    System.out.println("還剩票數:" + this.ticket--);
			    }			
		    }
		    return "票賣光了!";
	    }  
    }

main方法:
 
    public class ThreadDemo {  
	    public static void main(String[] args) {
		
		    ThreadCall thcal1 = new ThreadCall();
            ThreadCall thcal2 = new ThreadCall();
		    FutureTask<String> furtask1 = new FutureTask<String>(thcal1);
		    FutureTask<String> furtask2 = new FutureTask<String>(thcal2);
            
            new Thread(furtask1).start();
            new Thread(furtask2).start();

            try {
			    System.out.println("線程1的返回結果:" + furtask1.get());
			    System.out.println("線程2的返回結果:" + furtask2.get());
		    } catch (InterruptedException e) {
			    e.printStackTrace();
		    } catch (ExecutionException e) {
			    e.printStackTrace();
		    }		
		
	        }
        }
    
        

    多線程結束之後,各個線程的返回值通過 FutureTask 的父類   Future 中的 get() 方法來獲得返回值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章