天天看點

javaSE---多線程

多線程1

10.1.什麼是多線程?

  1. 什麼是程序?
javaSE---多線程

程序:在作業系統中運作的某個軟體/某個程式(主要是指在記憶體中)。[動态]

任何軟體/程式要運作都要被加載到記憶體中,而記憶體負責運作這個軟體/程式所需要的那些記憶體空間,就被稱為目前軟體在記憶體中的一個程序。

  1. 程序需要依賴于作業系統

    程序就是在作業系統中動态運作的靜态代碼。

  2. 什麼是線程?

    線程就是在作業系統中動态運作的靜态代碼【程序】中的某一項具體功能的執行過程【執行軌迹/執行線索】。

    例如:

    我們在window作業系統上打開“暴風影音”播放電影,此時“暴風影音”就會在window作業系統中産生一個程序;打開“暴風影音”播放電影的時候有畫面,聲音,中文字幕等等,這些畫面,聲音,中文字幕就是這個“暴風影音”程序中的多個線程。

    javaSE---多線程
程序 線程
依賴作業系統 依賴程序
程序與程序之間的互動很困難 線程與線程之間的互動很容易

3. 什麼是多線程?

多線程:某一個程式在運作的時候可能會産生多個不同的執行線索【執行軌迹】,這些多個不同的執行線索【執行軌迹】共同運作的情況就是多線程。

往往我們會感覺到這些多個不同的執行線索【執行軌迹】同時執行,實際上這時一種錯覺假象,實際上當這些多個不同的執行線索【執行軌迹】在運作的時候,某一個時刻隻有一個執行線索【執行軌迹】在運作,隻是這多個不同的執行線索【執行軌迹】快速的切換而已。

4.為什麼使用多線程?

1.使用多線程的目的就是為了提高程式的執行效率。

2.解決并發問題。

并行和并發有什麼差別?

并行:多個處理器或多核處理器同時處理多個任務。

并發:多個任務在同一個 CPU 核上,按細分的時間片輪流(交替)執行,從邏輯上來看那些任務是同時執行。

如下圖:【并發 = 兩個隊列和一台咖啡機】 【并行 = 兩個隊列和兩台咖啡機】

javaSE---多線程

10.2.多線程的建立方式以及差別

Java中的線程

當一個java程式啟動運作以後,至少有2個線程在運作。

  1. 主線程,就是java程式的主方法執行線索
  2. 垃圾回收線程。

Java中多線程的建立方式【4種】

第一種,通過繼承Thread類建立線程類

  1. 建立一個類,繼承Thread類
  2. 重寫run方法
  3. 将需要由線程執行的具體動作寫入run方法
package com.wangxing.test1;
/**
 *  1.建立一個類,繼承Thread類
 *  2.重寫run方法
 *  3.将需要由線程執行的具體動作寫入run方法
 * @author Administrator
 *
 */

public class TestThread extends Thread{
    //通過繼承Thread類建立線程類
	@Override
	public void run() {
		//得到目前線程的名稱
		String name=Thread.currentThread().getName();
		for(int i=0;i<=100;i++){
			System.out.println(name+"----i=="+i);
		}
	}
}

           

測試類:

package com.wangxing.test1;

public class testmain {

	public static void main(String[] args) {
		//啟動線程
		//1.建立線程對象
		//2.通過線程對象調用start方法啟動線程
		TestThread tth1=new TestThread();
		tth1.start();
		TestThread tth2=new TestThread();
		tth2.start();
		
	}

}

           

第二種,通過實作Runnable接口建立線程類

  1. 建立一個類,實作Runnable接口
  2. 重寫run方法
  3. 将需要由線程執行的具體動作寫入run方法
package com.wangxing.test2;
/**
 * 通過實作Runnable接口建立線程類
 * 1. 建立一個類,實作Runnable接口
 * 2. 重寫run方法
 * 3. 将需要由線程執行的具體動作寫入run方法
 * @author Administrator
 *
 */
public class MyThread implements Runnable {

	@Override
	public void run() {
		//得到目前線程的名稱
		String name=Thread.currentThread().getName();
		for(int i=0;i<=100;i++){
			System.out.println(name+"----i=="+i);
		}
	}
}

           

測試類:

package com.wangxing.test2;

public class TestMain {
	public static void main(String[] args) {
		/*
		 * 1.建立有線程類執行的目标對象【實作Runnable接口的java類對象】
		 * 2.建立線程對象【java.lang.Thread類的對象】
		 *  public Thread(Runnable target) 
		 *  public Thread(Runnable target, String name)
		 * 3.調用start方法啟動線程運作
		 */
		MyThread mt=new MyThread();
		Thread th1=new Thread(mt);
		th1.start();
		Thread th2=new Thread(mt);
		th2.start();
	}
}

           

第三種,通過Callable和Future接口建立線程

通過這兩個接口建立線程,你要知道這兩個接口的作用,下面我們就來了解這兩個接口:

通過實作Runnable接口建立多線程時,Thread類的作用就是把run()方法包裝成線程的執行體,那麼,是否可以直接把任意方法都包裝成線程的執行體呢?

從JAVA5開始,JAVA提供提供了Callable接口,該接口是Runnable接口的增強版,Callable接口提供了一個call()方法可以作為線程執行體,但call()方法比run()方法功能更強大,call()方法的功能的強大展現在:

1、call()方法可以有傳回值;

2、call()方法可以聲明抛出異常;

從這裡可以看出,完全可以提供一個Callable對象作為Thread的target,而該線程的線程執行體就是call()方法。

問題是:Callable接口是JAVA新增的接口,而且它不是Runnable接口的子接口,是以Callable對象不能直接作為Thread的target。還有一個原因就是:call()方法有傳回值,call()方法不是直接調用,而是作為線程執行體被調用的,是以這裡涉及擷取call()方法傳回值的問題。

于是,JAVA5提供了Future接口來代表Callable接口裡call()方法的傳回值,并為Future接口提供了一個FutureTask實作類,該類實作了Future接口,并實作了Runnable接口,是以FutureTask可以作為Thread類的target,同時也解決了Callable對象不能作為Thread類的target這一問題。

在Future接口裡定義了如下幾個公共方法來控制與它關聯的Callable任務:

1、boolean cancel(boolean mayInterruptIfRunning):試圖取消Future裡關聯的Callable任務;

2、V get():傳回Callable任務裡call()方法的傳回值,調用該方法将導緻程式阻塞,必須等到子線程結束以後才會得到傳回值;

3、V get(long timeout, TimeUnit unit):傳回Callable任務裡call()方法的傳回值。該方法讓程式最多阻塞timeout和unit指定的時間,如果經過指定時間後,Callable任務依然沒有傳回值,将會抛出TimeoutException異常;

4、boolean isCancelled():如果Callable任務正常完成前被取消,則傳回true;

5、boolean isDone():如果Callable任務已經完成, 則傳回true;

這種方式建立并啟動多線程的步驟如下:

1、建立Callable接口實作類,并實作call()方法,該方法将作為線程執行體,且該方法有傳回值,再建立Callable實作類的執行個體;
2、使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的傳回值;
3、使用FutureTask對象作為Thread對象的target建立并啟動新線程;
4、調用FutureTask對象的get()方法來獲得子線程執行結束後的傳回值。
           
package com.wangxing.test3;

import java.util.concurrent.Callable;

/**
 * 第三種方法:
 * 建立Callable接口實作類,并實作call()方法,該方法将作為線程執行體,
 * 且該方法有傳回值,再建立Callable實作類的執行個體;
 * 2、使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的傳回值;
 * 3、使用FutureTask對象作為Thread對象的target建立并啟動新線程;
 * 4、調用FutureTask對象的get()方法來獲得子線程執行結束後的傳回值。
 * @author Administrator
 *
 */
public class MyCallable implements Callable<String> {

	@Override
	public  String call() throws Exception {
		//得到目前線程的名稱
		String name=Thread.currentThread().getName();
		for(int i=1;i<=100;i++) {
			System.out.println(name+"--i=="+i);
		}
		return name+"執行完畢";
	}
}

           

測試類:

package com.wangxing.test3;

import java.util.concurrent.FutureTask;

public class TestMain {
/*
 * 1、boolean cancel(boolean mayInterruptIfRunning):試圖取消Future裡關聯的Callable任務;
 * 2、V get():傳回Callable任務裡call()方法的傳回值,調用該方法将導緻程式阻塞,必須等到子線程結束以後才會得到傳回值;
 * 3、V get(long timeout, TimeUnit unit):傳回Callable任務裡call()方法的傳回值。該方法讓程式最多阻塞timeout和unit指定的時間,如果經過指定時間後,Callable任務依然沒有傳回值,将會抛出TimeoutException異常;
 * 4、boolean isCancelled():如果Callable任務正常完成前被取消,則傳回true;
 * 5、boolean isDone():如果Callable任務已經完成, 則傳回true;
 */
	public static void main(String[] args)throws Exception {
		//目标對象
		MyCallable mc=new MyCallable();
		//将目标對象包裝成Runnable的子類
		   /*
		   FutureTask是RunnableFuture子類,
		   RunnableFuture是Runnable的子類
		   */
		FutureTask<Integer> task1=new FutureTask(mc);
		FutureTask<Integer> task2=new FutureTask(mc);
        //線程對象
		Thread Th1=new Thread(task1);
		Thread Th2=new Thread(task2);
		//調用start方法啟動線程
		Th1.start();
		Th2.start();
		
		//測試方法
		//5、boolean isDone():如果Callable任務已經完成, 則傳回true;
		
			//2、V get():傳回Callable任務裡call()方法的傳回值,調用該方法将導緻程式阻塞,必須等到子線程結束以後才會得到傳回值;
			Object  returntask1=task1.get();
			System.out.println(returntask1);
			Object  returntask2=task2.get();
			System.out.println(returntask2);
	}

}

           

第四種,通過線程池建立多線程【使用的比較少,是以不強調】

差別:

繼承Thread類 實作Runnable接口 Callable和Future接口
建立新類繼承Thread類重寫run方法 建立新類實作Runnable接口重寫run方法 建立新類實作Callable接口重寫call方法,注意Callable接口的泛型類型
run方法沒有傳回值,不能聲明抛出異常 call方法有傳回值,通過Future接口提供的get方法得到傳回值,可以聲明抛出異常
建立Thread類的子類對象【線程對象】,通過子類對象調用start方法啟動線程 建立實作實作Runnable接口的子類對象【目标對象】,通過Thread的構造方法,關聯目标對象,建立線程對象【Thread類的對象】,通過線程對象調用start方法啟動線程 建立實作Callable接口的子類對象【目标對象】,通過Future接口的子類FutureTask将目标對象包裝成Runnable接口的子類對象,通過Thread的構造方法,關聯FutureTask包裝成的Runnable接口的子類對象,建立線程對象【Thread類的對象】,通過線程對象調用start方法啟動線程
無法資源共享 可以資源共享 可以資源共享
不考慮資源共享時 考慮資源共享時 考慮資源共享時,異步程式設計

資源是否共享:

繼承Thread類:

package com.wangxing.test4;

public class TestThread extends Thread{
	private int piao=5;
	public  void run(){
		boolean flag=true;
		while(flag){
			if(piao>0){
				piao =piao-1;
				System.out.println(Thread.currentThread().getName()+"賣出一張票,還剩"+piao+"張");
			}
			else{
				flag=false;
			}
		}
		
	}
}

           

測試類:

package com.wangxing.test4;

import java.util.concurrent.FutureTask;

public class TestMain {

	public static void main(String[] args) {
		//第一種多線程---資源不共享
		TestThread Th1=new TestThread();
		TestThread Th2=new TestThread();
		Th1.start();
		Th2.start();
	}

}
           
javaSE---多線程

實作Runnable接口:

package com.wangxing.test4;

public class MyThread implements Runnable{
	private int piao=5;
	@Override
	public void run() {
		boolean flag=true;
		while(flag){
			if(piao>0){
				piao =piao-1;
				System.out.println(Thread.currentThread().getName()+"賣出一張票,還剩"+piao+"張");
			}
			else{
				flag=false;
			}
		}
	}
	
}

           

測試類:

package com.wangxing.test4;

import java.util.concurrent.FutureTask;

public class TestMain {

	public static void main(String[] args) {
		
		//第二種多線程---資源共享
		MyThread mt=new MyThread();
		Thread th1=new Thread(mt);
		th1.start();
		Thread th2=new Thread(mt);
		th2.start();
	}

}
           
javaSE---多線程

實作Callable和Future接口:

package com.wangxing.test4;

import java.util.concurrent.Callable;

public class MyCallable implements Callable{
	private int piao=5;
	@Override
	public Object call() throws Exception {
		boolean flag=true;
		while(flag){
			if(piao>0){
				piao =piao-1;
				System.out.println(Thread.currentThread().getName()+"賣出一張票,還剩"+piao+"張");
			}
			else{
				flag=false;
			}
		}
		return null;
	}
	
}

           

測試類:

package com.wangxing.test4;

import java.util.concurrent.FutureTask;

public class TestMain {

	public static void main(String[] args) {	
		//第三種多線程---資源共享
		MyCallable mc=new MyCallable();
		FutureTask fu=new FutureTask<>(mc);
		Thread th1=new Thread(fu);
		Thread th2=new Thread(fu);
		th2.start();
		th1.start();
	}

}
           
javaSE---多線程