多線程的實現

 在一個進程上可以劃分出若干個線程,那麼線程的操作一定要比進程更快些,所以多線程的操作性能要超過多進程的操作,但是所有的線程都一定是要在進程的基礎之上進行劃分。

所以進程一旦消失,線程一定消失。線程依附於進程存在。


一.多線程的實現

 在java中對於多線程實現一定要有一個線程的類。

    線程主類的實現必須繼承Thread父類或實現Runnable接口(Callable接口)


  1. 繼承Thread類實現多線程

    在java中存在有Thread類,子類繼承Thread類之後需要覆寫Thread類的Run()方法,這個Run()方法就屬於線程的主方法。Run()的定義:public void run()

範例:實現線程的主體類

class MyThread extends Thread{ //表示實現多線程
    private String name;
    public MyThread(String name){ //線程名字
        this.name = name;
    }
    @Override
    public void run() { //覆寫Run()方法,線程的主方法
        for(int i=0;i<10;i++){
            System.out.println(this.name+";i= "+i);
        }
    }
}

在線程的主類之中只是將內容輸入10次,但是需要注意:所有的多線程的執行一定是並非完成的,既:在同一個時間段上有多個線程交替執行,所以爲了達到這樣的目的,絕對不能直接調用Run()方法,而是通過調用Thread類中的start()方法啓動多線程。 

範例:啓動多線程

public class test {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("線程A");
        MyThread mt2 = new MyThread("線程B");
        MyThread mt3 = new MyThread("線程C");
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

 所有的線程都屬於交替執行,本身是沒有執行順序的。


爲什麼現在啓動多線程不使用Run()方法,而非要使用start()方法,需要了解可以從Thread類中的start()源碼來解析(java安裝目錄下的源碼文件src.zip)。

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    group.add(this);
    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
        }
    }
}
private native void start0();

 從代碼可以看出,方法會拋出一個異常:IllegalThreadStateException,但是整個方法沒有使用throws聲明,沒有使用try...catch()捕獲處理,因爲該異常屬於RuntimeException的子類。

java.lang.Object

  - java.lang.Throwable

      - java.lang.Exception

          - java.lang.RuntimeException

              - java.lang.IllegalArgumentException

                  - java.lang.IllegalThreadStateException

     此異常是指一個線程已經調用了start()方法後又重複執行start()方法所造成的。

在調用start()方法裏面發現會調用start0()方法,而start0()方法上使用了native關鍵字定於,這個關鍵字指的是要調用本機的操作系統函數。


總結:只有Thread類的start()方法才能夠進行操作系統資源的分配,所以啓動多線程的方式永遠是調用Thread類的start()方法實現。


2.Runnable接口實現多線程。

    使用Thread類會產生單繼承的侷限性,所以最好使用Runnable接口來實現多線程。

  Runnable接口的定義結構:

    @FunctionalInterface
    public interface Runnable {
        public void run();
    }

 @FunctionalInterface 表示函數式接口,所以可以利用lamda表達式完成。


範例:正常思路實現多線程

class MyThread  implements Runnable{ //表示實現多線程
  private String name;
  public MyThread(String name){ //線程名字
    this.name = name;
    }
    @Override
    public void run() { //覆寫Run()方法,線程的主方法
    for(int i=0;i<10;i++){
        System.out.println(this.name+";i= "+i);
    }
   }
}

 要想啓動多線程只能是依靠Thread類中的start()方法,之前繼承Thread類時可以直接將start()方法繼承下來使用。

但現在實現的是Runnable接口,沒有start()方法了,所以要想啓動多線程,需使用到Thread類中的構造方法:public Thread(Runnable target)  此構造方法可以接受Runnable的子類對象。

啓動多線程如下:

public class test {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("線程A");
        MyThread mt2 = new MyThread("線程B");
        MyThread mt3 = new MyThread("線程C");
        new Thread(mt1).start();
        new Thread(mt2).start();
        new Thread(mt3).start();
    }
}


爲了方便實現,可以直接使用匿名內部類或者是Lamda實現代碼。

範例:匿名內部類實現

public class test {
	public static void main(String[] args) {
		String name = "線程對象";
		new Thread(new Runnable(){
			@Override
			public void run() {
				for(int i=0;i<10;i++){
					System.out.println(name+";i= "+i);
				}
			}
		}).start();
	}
}


範例:jdk1.8使用Lamda實現

public class test {
	public static void main(String[] args) {
		String name = "線程對象";
		new Thread(()->{
			for(int i=0;i<10;i++){
				System.out.println(name+";i= "+i);
			}
		}).start();
	}
}

 只要給出的是函數式接口基本上就可以使用Lamda表達式或者是方法引用。


3.Thread類和Runnable接口實現方式的區別

首先來觀察Thread類的定於結構:

    public class Thread extends Object implements Runnable

  可以看出Thread類實現了Runnable接口。


除了以上的繼承關聯之外還有一點區別:Runnable接口實現的多線程要比Thread類實現的多線程更方便的表示出數據共享的概念。


範例:希望有三個線程進行賣票--Thread實現。

package org.com;
class MyThread extends Thread{ //表示實現多線程
	private int ticket=5;
	@Override
	public void run() { //覆寫Run()方法,線程的主方法
		for(int i=0;i<50;i++){
			if(this.ticket>0){
				System.out.println("賣票; ticket = "+this.ticket--);
			}
		}
	}
}
public class test {
	public static void main(String[] args) {
		MyThread mt1 = new MyThread();
                MyThread mt2 = new MyThread();
                MyThread mt3 = new MyThread();
                mt1.start();
                mt2.start();
                mt3.start();
	}
}

三個棧內存對應着三個堆內存,所以 輸出:

賣票; ticket = 5

賣票; ticket = 5

賣票; ticket = 5

賣票; ticket = 4

賣票; ticket = 4

賣票; ticket = 3

賣票; ticket = 2

賣票; ticket = 1

賣票; ticket = 4

賣票; ticket = 3

賣票; ticket = 2

賣票; ticket = 1

賣票; ticket = 3

賣票; ticket = 2

賣票; ticket = 1


範例:Runnable實現

package org.com;
class MyThread implements Runnable{ //表示實現多線程
	private int ticket=5;
	@Override
	public void run() { //覆寫Run()方法,線程的主方法
		for(int i=0;i<50;i++){
			if(this.ticket>0){
				System.out.println("賣票; ticket = "+this.ticket--);
			}
		}
	}
}
public class test {
	public static void main(String[] args) {
		MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
	}
}

輸出:

賣票; ticket = 5

賣票; ticket = 3

賣票; ticket = 4

賣票; ticket = 1

賣票; ticket = 2


 總結:使用Runnable接口可以比Thread類更好的實現數據共享操作,並且使用Runnable接口可以避免單侷限性問題。


4.實現Callable接口

    Callable接口是從JDK1.5之後開始有的。這個接口裏面比Runnable接口唯一的強大之處在於它可以返回執行結果。此接口在java.util.concurrent包中定義。

@FunctionalInterface
public interface Callable<V> {
	public V call() throws Exception;
}

泛型表示的是返回值類型。call()方法就相當於run()方法。


範例:定於線程主題類。

class MyThread implements Callable<String>{ //表示實現多線程
	private int ticket=5;
	@Override
	public String call() { //覆寫Run()方法,線程的主方法
		for(int i=0;i<50;i++){
			if(this.ticket>0){
				System.out.println("賣票; ticket = "+this.ticket--);
			}
		}
		return "票賣完了";
	}
}

 由於Thread類中並沒有提供接收Callable接口的對象操作,所以要啓動多線程,需要通過一些繼承結構。

 java.util.concurrent.FutureTask<V>的定於結構:

    public class FutureTask<V>extends Objectimplements RunnableFuture<V>

FutureTask<V>實現了RunnableFuture接口,java.util.concurrent.RunnableFuture<V>的定於結構:
    public interface RunnableFuture<V>extends Runnable, Future<V>

 繼承了Runnable接口和Future<V>接口,而Future<V>接口裏有get()方法。


所以啓動多線程範例:

public class test {
	public static void main(String[] args) throws Exception {
		Callable<String> cal = new MyThread();
		FutureTask<String> task = new FutureTask<>(cal);//取得執行結果
		Thread thread = new Thread(task);
		thread.start();
        System.out.println(task.get()); //取得線程主方法的返回值
	}
}

輸出:

賣票; ticket = 5

賣票; ticket = 4

賣票; ticket = 3

賣票; ticket = 2

賣票; ticket = 1

票賣完了


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