1. 程式人生 > >淺談對多線程的理解(一)

淺談對多線程的理解(一)

cpu 動態 tor 包裝 list star and jvm 周期

今天我們先來聊聊有關線程的話題......

一. 線程概述

1. 簡單區分程序、進程和線程

程序是指一段靜態的代碼

進程是指正在執行的程序,將靜態的代碼運行起來

線程是指正在執行程序的小單元

舉個栗子,班級準備大掃除,在大掃除之前,老師在紙上列了一個清單,每個同學都有不同的工作任務,分配好任務之後,每個同學都是有條不紊地完成自己的任務,掃地的同學去掃地,擦黑板的同學去擦黑板,清理桌子的同學清理桌子......在這個例子裏,這個清單就是程序,而這個班級的全體同學是一個整體,也就是一個進程,最後,這個班級裏面一個個同學就是一個個線程。


2. 理解進程

理解線程之前,先簡單理解一下進程。進程的三大特征:獨立性、動態性、並發性。

獨立性:指進程是系統中獨立存在的實體,擁有獨立的資源(eg:私有的地址空間)。

動態性:這是相對於程序而言的,程序是一段靜態的代碼,而進程是活動的,擁有自己的生命周期。

並發性:多個進程可以在單個處理器上並發執行,互不影響。

還是上面那栗子,這個班級就是一個進程,他是一個整體,他擁有自己的教室,有自己的班級名字,這裏可以體現出獨立性。這個班級的全體人員按照的任務清單幹活,直至把教室打掃幹凈(即完成任務),這裏可以體現出動態性。並發性呢,首先這個班級不只有一個,還有好多其他的班級,他們也可以打掃他們自己的教室,互不影響。


3. 理解線程

線程是進程的執行單元,在程序中,線程是獨立的、並發的執行流。

線程的特點:

  1. 每個線程有自己的堆棧,自己程序計數器,自己的局部變量,這裏體現了線程的獨立性。

  2. 相同父進程下的所有線程共享進程獨立的內存單元(eg:代碼段、進程的共有數據),為此可以實現線程間的相互通信。

  3. 多個線程之間也可以並發執行,互不影響。


4. 多線程 VS 多進程

  1. 線程之間可以共享內存,而進程之間不可以。

  2. 系統創建線程代價比較小,而且多線程是實現多任務並發比多進程的效率更高。

  3. Java語言內置了多線程功能,簡化了Java多線程編程。


二. 線程的創建和啟動

1. 繼承Thread類創建線程類

步驟:

  1. 定義一個線程類,需繼承Thread類。

  2. 重寫父類的run( )方法,此方法是線程執行體,供cpu自動調用(cpu會用調度策略去處理就緒狀態的線程)。

  3. 創建線程類的實例對象,調用start( )方法,這個方法告訴cpu這個線程對象進入就緒狀態。

 1 package com.hx.thread;
 2 ?
 3 // 1.定義一個線程類,需繼承Thread類。
 4 public class MyThread1 extends Thread {
 5     // 2.重寫run方法
 6     public void run() {
 7         for (int i = 0; i < 100; i++) {
 8             System.out.println(Thread.currentThread().getName() + " " + i);
 9             try {
10                 Thread.sleep(10);
11             } catch (Exception e) {
12                 e.printStackTrace();
13             }
14         }
15     }
16     
17     public static void main(String[] args) throws Exception {
18         // 創建線程實例,調用start方法,進入就緒狀態,交給cpu
19         MyThread1 myThread1 = new MyThread1();
20         myThread1.start();
21         for (int i = 0; i < 100; i++) {
22             System.out.println(Thread.currentThread().getName() + "     " + i);
23             Thread.sleep(10);
24         }
25     }
26 }

2. 實現Runnable接口創建線程類

步驟:

  1. 定義一個線程類,需實現Runnable接口。

  2. 實現接口的run( )方法,此方法是線程執行體,供cpu自動調用(cpu會用調度策略去處理就緒狀態的線程)。

  3. 創建線程類的實例對象。可是Runnable沒有start( )方法,因此需要第4步。

  4. 創建一個Thread對象(真正的線程對象),用來包裝上面的那個實例對象,然後調用start( )方法。

 1 package com.hx.thread;
 2 ?
 3 // 1.定義一個線程類,需實現Runnable接口。
 4 public class MyThread2 implements Runnable {
 5     // 2.實現接口的run( )方法
 6     @Override
 7     public void run() {
 8         for (int i = 0; i < 100; i++) {
 9             System.out.println(Thread.currentThread().getName() + " " + i);
10             try {
11                 Thread.sleep(10);
12             } catch (Exception e) {
13                 e.printStackTrace();
14             }
15         }
16     }
17 ?
18     public static void main(String[] args) throws Exception {
19         // 3.創建線程類的實例對象
20         MyThread2 myThread2 = new MyThread2();
21         // 4.創建一個Thread對象(真正的線程對象),用來包裝上面的那個實例對象,然後調用start( )方法。
22         Thread t = new Thread(myThread2);
23         t.start();
24         for (int i = 0; i < 100; i++) {
25             System.out.println(Thread.currentThread().getName() + "     " + i);
26             Thread.sleep(10);
27         }
28     }
29 }

3. 實現Callable接口創建線程類

步驟:

  1. 定義一個線程類,需實現Callable接口。

  1. 實現Callable接口的call( )方法,此方法是線程執行體。

  1. 創建線程類的實例對象。

  1. 創建FutureTask的對象來包裝線程類實例對象。

  1. 創建Thread的對象來包裝Future類的實例對象。

 1 package com.hx.thread;
 2 import java.util.concurrent.Callable;
 3 import java.util.concurrent.FutureTask;
 4 ?
 5 // 1.定義一個線程類,需實現Callable接口
 6 public class MyThread3 implements Callable {
 7     // 2.實現Callable接口的call()方法
 8     @Override
 9     public String call() throws Exception {
10         for (int i = 0; i < 100; i++) {
11             System.out.println(Thread.currentThread().getName() + " " + i);
12             Thread.sleep(10);
13         }
14         return Thread.currentThread().getName();
15     }
16 ?
17     public static void main(String[] args) throws Exception {
18         // 3.創建線程類的實例對象
19         MyThread3 myThread3 = new MyThread3();
20         // 4.創建FutureTask的實例對象來包裝線程類實例對象
21         FutureTask futureTask = new FutureTask(myThread3);
22         // 5.創建Thread的實例對象來包裝Future類的實例對象
23         Thread t = new Thread(futureTask);
24         t.start();
25         for (int i = 0; i < 100; i++) {
26             System.out.println(Thread.currentThread().getName() + "     " + i);
27             Thread.sleep(10);
28         }
29         // 打印出call()方法的返回值
30         System.out.println(futureTask.get());
31     }
32 }

4. 三種方式的對比

  1. 采用繼承Thread類這種方式來創建線程,編寫簡單,可是由於Java不支持多繼承,所以不能再繼承其他父類。

  2. 采用實現Runnable接口或Callable接口,可以繼承其他類,多個線程可以共享同一個target對象,非常適合多個線程來處理同一資源的情況,可以更好地體現面向對象的特點,不過編寫比較復雜。

  3. 采用實現Callable接口,call( )方法是線程執行體,有返回值,可以拋出異常,其功能比run( )方法更強大。


三. 線程的生命周期

技術分享圖片


四. 控制線程

Thread類的工具方法

join( ):讓一個線程等待另外一個線程完成的方法,當在某個程序執行流中調用其他線程的join方法,調用線程將被阻塞,直至join線程完成。

setDaemon(true):指定線程設置為後臺線程。在start( )之前調用。後臺線程,又稱守護線程、精靈線程,起作用是為其他線程提供服務(eg:JVM的垃圾回收線程),如果所有的前臺線程都死亡,後臺線程也會自動死亡。

sleep( long time):設置線程睡眠時間,參數單位為毫秒,調用此方法線程進入阻塞狀態。

setPriority(int newPriority):設置優先級,參數範圍:1-10,一般使用三個靜態常量(MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY)。


嘻嘻,今天的內容就先到這吧,歡迎大家前來留言。

由於現在個人水平有限,文章若存不當之處,還請各位大佬們加以斧正。

淺談對多線程的理解(一)