多線程

来源:http://www.cnblogs.com/tonghun/archive/2017/06/03/6935659.html
-Advertisement-
Play Games

一 概述 1.什麼是進程? 進程是一個相對獨立的執行單位。 2.什麼是線程? 進程的一部分,進程中實際的任務執行者,必須依附於進程。線程對進程的依賴主要體現在: 線程不能脫離進程開啟,必須在進程開啟的前提下開啟。 線程有時必須從進程中獲取數據。 3.線程與進程的區別? 線程與進程是兩個相對的概念,一 ...


 

一 概述

1.什麼是進程?

進程是一個相對獨立的執行單位。

2.什麼是線程?

進程的一部分,進程中實際的任務執行者,必須依附於進程。線程對進程的依賴主要體現在:

  • 線程不能脫離進程開啟,必須在進程開啟的前提下開啟。
  • 線程有時必須從進程中獲取數據。

3.線程與進程的區別?

線程與進程是兩個相對的概念,一個對象相對於它擁有的執行單位被稱為進程,從自身所屬的上級執行者來看,又被稱作線程。

4.多線程的設計目的、用途、意義

CUP在任何一個時間點都只能執行一個線程,多線程的本質是多個任務高速交替執行。如果多個線程間不存在數據交換,可以單獨執行,採用多線程並不能減少總的執行時間。

多線程設計的主要目的不是為了提高執行速度,而是相對平均地執行每一個線程,不致使某一個線程長時間持有CPU時間片,其他線程長時間處於等待狀態。由於CPU時間片在多個線程間切換迅速,超出了人類感官所能察覺的範圍,所以感覺多個任務都是執行。

例如,當多個人訪問同一個網站,每一個人都需要5分鐘,如果不採用多線程,同時只允許一個人進入網站,其他多數人都要等待5分鐘,用戶體驗很差。這是採用多線程,一個人進入以後,CPU轉向其他用戶,讓其他用戶陸續進入,用戶體驗就提高了,儘管總的執行時間並沒有減少。

5.CPU調度模式

  • 分時調度模式:系統平均地為各個線程分配CPU時間片。
  • 搶占式調度模式:各個線程搶奪CPU時間片,CPU時間片線上程間不均勻分配。

二 線程創建

1.Java SE API 提供了兩種創建線程的方式:

  • 實現Runnable介面,將實現類的對象作為參數傳入Thread的構造器中。
  • 直接繼承Thread類。

2.無論採用哪種方式,需要執行的任務都必須放在run方法中。

3.兩種創建方式的區別:

⑴Java採用單繼承,即一個類只能繼承一個父類,而允許一個類實現多個介面,採用繼承Thread的方式創建線程,就使本類失去了唯一的一次繼承機會。

⑵實現資源共用的方式不同

  • 首先需要明確的一點,通過繼承Thread創建線程的方式也可以實現資源共用,只是由於通過new關鍵字創建的多個線程是不同的對象,那麼共用資源只能來自於外部,通常通過構造器註入。
  • 而通過實現Runnable介面的方式創建線程,可以利用同一個實現類對象創建多個線程,實現了資源共用,共用資源來自線程內部。

4.採用實現Runnable介面的方式創建線程,不僅保留了唯一的繼承機會,而且在實現資源共用的操作相對簡單,所以一般採用該方式創建線程。

5.通過繼承Thread的方式實現資源共用:

提供共用資源的外部類

package com.test.thread.extendsThread;

public class MyClass {

    public int count;

}

Thread線程子類

package com.test.thread.extendsThread;

public class MyThread extends Thread {
    private MyClass obj;

    public MyThread() {
        super();
    }

    public MyThread(MyClass obj) {
        super();
        this.obj = obj;
    }

    @Override
    public void run() {
        System.out.println("obj=" + obj);
        while (true) {
            synchronized (obj) {
                if (obj.count > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "----當前數量=" + obj.count--);
                } else
                    return;
            }
        }

    }

}

測試類

package com.test.thread.extendsThread;

import org.junit.Test;

import com.test.thread.synchronizedTest.demo02.MyTestRunnable;

import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
import net.sourceforge.groboutils.junit.v1.TestRunnable;

public class ThreadExtendsTest {

    /**
     * JUnit單元測試不支持多線程測試,使用GroboUtils進行多線程測試(導入架包)
     * 
     * @throws Throwable
     */
    @Test
    public void test01() throws Throwable {
        MyClass obj = new MyClass();
        obj.count = 10;
        MyThread myth01 = new MyThread(obj);
        MyThread myth02 = new MyThread(obj);
        MyThread myth03 = new MyThread(obj);
        MyTestRunnable t01 = new MyTestRunnable(myth01);
        MyTestRunnable t02 = new MyTestRunnable(myth02);
        MyTestRunnable t03 = new MyTestRunnable(myth03);
        TestRunnable[] tr = new TestRunnable[3];
        tr[0] = t01;
        tr[1] = t02;
        tr[2] = t03;
        MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(tr);
        mttr.runTestRunnables();
    }

    // 放在主線程中測試
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.count = 10;
        MyThread t01 = new MyThread(obj);
        MyThread t02 = new MyThread(obj);
        MyThread t03 = new MyThread(obj);
        t01.setName("t01");
        t02.setName("t02");
        t03.setName("t03");
        t01.start();
        t02.start();
        t03.start();
    }
}

 

三 線程生命周期

1.什麼是線程的生命周期?

由不同階段構成的線程從出生到死亡的整個過程,叫做線程的生命周期。

2.線程生命周期的意義

瞭解線程的生命周期能夠更好地掌握線程的運行情況,比如線程的就緒狀態,意味著不是調用start方法之後,線程立即執行。

3.生命周期的幾個階段:

  • 出生狀態:線程創建完成,尚未開啟前的狀態。
  • 就緒狀態:調用start方法開啟線程,線程尚未運行的狀態。
  • 運行狀態:線程獲取CPU時間片執行時的狀態。
  • 休眠狀態:線程調用sleep方法後進入指定時長的休眠狀態,時間結束進入就緒狀態。
  • 等待狀態:監聽對象線上程內部調用wait方法後,線程失去對象鎖,進入等待狀態。
  • 阻塞狀態:線程發出輸入或者輸出請求後進入阻塞狀態。
  • 死亡狀態:run方法執行完畢,線程死亡。

四 線程的加入

一個線程A在另一個線程B內部調用join方法,B線程中止,A線程開始執行,A線程執行完畢,B線程才開始執行。

五 線程優先順序

線程優先順序設定了線程獲取CPU時間片的概率,僅僅是一種概率,不能保證優先順序高的線程一定優先獲得CPU時間片。

線程優先順序分為10個等級,從1-10,數值越大,優先順序越高,通過setProprity(int)方法設置。

六 線程禮讓

Thread.yield,線程禮讓只是通知當前線程可以將資源禮讓給其他線程,並不能保證當前線程一定讓出資源。

七 同步機制

1.什麼是線程同步機制?

使得同一資源同一時刻只能有一個線程訪問的安全機制,即一個線程訪問完畢,其他線程才能訪問。

2.同步機制的目的

由於目標資源同一時刻只有一個線程訪問,解決了線程安全問題。

3.什麼是線程安全問題?

⑴線程安全問題產生條件

  • 多線程併發訪問。
  • 存在可修改的共用數據。

⑵第一個線程獲取了共用數據,操作結束前,第二個線程修改了該數據,導致第一個線程運算時採用的不是獲取時的數據。

4.同步機制解決線程安全問題的原理

synchronized(共用對象){ 修改共用數據的代碼 }

上述操作給修改共用數據的代碼加了一把對象鎖。任何一個對象只有一把對象鎖,線程只有獲得了對象鎖才能訪問加鎖的資源。一個線程獲取了對象鎖,執行加鎖的代碼,執行完畢,歸還對象鎖,其他線程開始爭奪對象鎖,訪問資源。

5.類鎖

synchronized關鍵字加到靜態方法上時,形成類鎖,執行該方法上必須獲取類鎖。

類鎖與對象鎖是兩種不同的鎖,允許一個線程持有類鎖,另一個線程持有對象鎖。

6.synchronized關鍵字

synchronized關鍵字加在成員方法,該方法成為同步成員方法,由於一個對象只有一把對象鎖,一個線程訪問了一個同步成員方法,其他線程不能訪問其他同步成員方法。

同步方法不可以被繼承,同步方法在子類中失去同步機制。

7.判斷條件的設置

在同步機制中,如果同步代碼的執行需要滿足一定條件,那麼將判斷條件放在鎖內,保證當前獲取了鎖的線程在執行同步代碼時滿足執行條件。如果放在鎖外,有可能出現當前線程獲取了鎖以後不滿足執行條件的情況。

不存線上程安全問題的做法:

public void run() {
        System.out.println("obj=" + obj);
        while (true) {
            synchronized (obj) {
                if (obj.count > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "----當前數量=" + obj.count--);
                } else
                    return;
            }
        }

    }

如果將判斷條件obj.count>0放在while語句中,可能出現某個線程進入while語句時count為1,滿足條件,進入,等待獲取對象鎖。當前持有對象鎖的線程執行完畢,count變為0,等待線程獲取對象鎖,在count=0的情況下執行同步塊,判斷條件失效。

 

八 死鎖

1.什麼是死鎖?

線程A需要多把鎖,線程B持有A缺少的鎖,缺少A持有的鎖,由於線程在獲取到全部的鎖之前不會釋放持有的鎖,這使得線程A與線程B陷入僵持,整個進程處於停滯狀態。

2.怎麼避免死鎖?

減少同步機制中鎖的數目,儘量避免同一把鎖出現在多處。

九 守護線程

1.用戶線程?

一般情況下創建的線程都是用戶線程,即該線程未被顯式設定為守護線程,未在守護線程內部創建。

2.主線程屬於用戶線程。

3.什麼是守護線程?

運行在後臺、為用戶線程提供服務的線程。

4.守護線程創建

用戶線程調用setDaemon(true)方法,或者在守護線程內部創建線程。

5.守護線程的作用

守護線程用於為用戶線程提供服務,如垃圾回收器。

6.JVM在所有的用戶線程執行完畢後終止,無論此時守護線程是否執行完畢。

7.守護線程運行在後臺,所有用戶線程結束後,自動結束。

十 wait與sleep方法對比

1.存在範圍

  • wait方法是Object級的,即java中的任何一個對象都擁有該方法,像toString一樣。
  • sleep方法只在Thread及其子類中存在。

2.作用

  • sleep使當前線程休眠,釋放CPU時間片,不會釋放持有的鎖。
  • wait用於線程間通信,由對象管理所有以該對象為鎖的全部線程。在同步代碼中由鎖對象調用,使當前線程釋放持有的對象鎖。

3.使用方法

  • sleep方法是一個靜態方法,直接通過Thread調用,Thread.sleep。
  • 用在同步代碼中,由鎖對象調用。

4.相關方法

  • obj.notify():隨機喚醒對象監聽器上的一個線程,該線程進入就緒狀態,一旦獲得對象鎖與CPU時間片,從等待處接著執行,不是重新進入run方法或者同步代碼中。
  • obj.notifyAll():喚醒對象監聽器上所有的等待線程,使它們全部進入就緒狀態。

十一 ThreadLocal

1.線程局部變數,為每一個線程提供一個變數的副本,使得各個線程相對獨立地操作變數,避免線程安全問題。

2.首先必須明確一點,ThreadLocal.get()獲取的變數副本必須手動傳入:

ThreadLocal.set(Object obj)

初次獲取時,判斷線程局部變數中是否保存有變數副本,如果沒有則手動傳入,在該線程中下次獲取的就是初次傳入的對象。

3.ThreadLocal的目的是保證在一個線程內部,一次創建,多次獲取。

4.基本原理:
將初次傳入的變數與線程綁定,線程不變,變數不變。

十二 GroboUtils多線程測試

1.JUnit測試不支持多線程,GroboUtils提供了對多線程測試的支持,使用時需要導入架包。

2.幾個比較重要的類:

  • TestRunnable:實現了Runnable介面,run方法中運行的是runTest方法,runTest方法是一個抽象方法。
  • MultiThreadedTestRunner:負責管理並開啟多個線程。

3.測試步驟

⑴繼承TestRunnable,實現其中的抽象方法runTest,將需要運行的代碼放入該方法中。通常為子類定義一個有參構造方法,方法形參為需要測試的線程,在runTest方法中調用測試線程的run方法,從而將將需要執行的代碼註入runTest方法中。

⑵創建測試線程數組,將需要測試的TestRunnable實現類傳入其中:

TestRunnable[] tr=new TestRunnable[len];

⑶根據測試線程數組創建線程管理與運行對象並開啟多線程:

MultiThreadedTestRunner mttr=new MultiThreadedTestRunner(tr);
mttr.runTestRunnables();

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 源碼支持QQ 1039318332 源碼地址:http://git.oschina.net/sunzewei/EIP 一、更新記錄1、更新日期:2017-02-24 00:00:002、更新內容: 版本升級:V2.0 1、支持多資料庫,可在實體類指定具體操作資料庫,可實現系統分庫操作 2、重新整理架 ...
  • 本章和大家分享的內容是使用Signal R框架創建個簡易的群聊功能,主要講解如何在.Net的MVC中使用這個框架,由於這個項目有官方文檔(當然全英文),後面也不打算寫分享篇了,主要目的是讓朋友們在需要使用Web實時通信的時候有更多一種解決方案,畢竟這是微軟主推的一種解決方案之一。 SignalR網上 ...
  • NuGet包地址: https://www.nuget.org/packages/OYMLCN.WeChat.Core 由於來的OYMLCN.WeChat存在深度封裝,並沒有做完整的測試,對於使用不友好,現已重新構建SDK的接收消息被動回覆模塊。 現已做到最大程度的簡易化及模塊化,每個模塊都已完成單 ...
  • 回到目錄 在MVC,EF,LINQ環境里,我們經常會用到DataModel(DO)和ViewModel(VO),可能對於它們的屬性校驗我們會採用特性的方式,當然這很直觀,就連微軟的DEMO也是如些,一般是這樣的代碼 而這種設計方式給我們以後的維護帶來很多問題,具體大叔總結一下: 綜上所述,Fluen ...
  • Java基礎十二--多態是成員的特點 一、特點 1,成員變數。 編譯和運行都參考等號的左邊。 覆蓋只發生在函數上,和變數沒關係。 Fu f = new Zi();System.out.println(f.num);//是父類,答案是3 2,成員函數(非靜態)。 編譯看左邊,運行看右邊。 因為成員函數 ...
  • 頭文件algorithm中的常用函數 一、非修改性序列操作(12個) 迴圈 對序列中的每個元素執行某操作 for_each() 查找 在序列中找出某個值的第一次出現的位置 find() 在序列中找出符合某謂詞的第一個元素 find_if() 在序列中找出一子序列的最後一次出現的位置 find_end ...
  • 慕課網Hibernate初探之一對多映射實驗及總結 一、本課核心 * 1、如何在MyEclipse中使用Hibernate * 2、如何實現Hibernate中一對多的映射 * 3、如何創建Session對象 * 4、Hibernate如何使用增刪改查 1、如何在MyEclipse中使用Hibern ...
  • 學習一門技術,不止要會,還要善用,例子就是帶你快速入門的最佳利器。本文就是要用例子,不,大量的例子來帶你走進PowerShell應用世界。 本文主要介紹一些PowerShell入門的基礎知識,對技術小白來說可以快速入門,對技術老鳥來說可以複習鞏固,廢話不多說,直接進入正題。 PowerShell,相 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...