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

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

 

一. 線程概述

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

程序是指一段靜態的代碼編程

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

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

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


2. 理解進程

理解線程以前,先簡單理解一下進程。進程的三大特徵:獨立性、動態性、併發性。工具

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

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

併發性:多個進程能夠在單個處理器上併發執行,互不影響。code

仍是上面那栗子,這個班級就是一個進程,他是一個總體,他擁有本身的教室,有本身的班級名字,這裏能夠體現出獨立性。這個班級的全體人員按照的任務清單幹活,直至把教室打掃乾淨(即完成任務),這裏能夠體現出動態性。併發性呢,首先這個班級不僅有一個,還有好多其餘的班級,他們也能夠打掃他們本身的教室,互不影響。對象


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         // 3.建立線程實例,調用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)。


 

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

因爲如今我的水平有限,文章若存不當之處,還請各位大佬們加以斧正。

相關文章
相關標籤/搜索