併發編程之線程第一篇

来源:https://www.cnblogs.com/haizai/archive/2020/02/07/12274414.html
-Advertisement-
Play Games

併發編程之線程第一篇 3.4 原理之線程運行 線程上下文切換(Thread Context Switch) 3.5 常見方法 3.6 start與run 3.7 sleep與yield 案例 - 防止CPU占用100% 3.8 join方法詳解 3.9 interrupt方法詳解 兩階段終止模式 3 ...


併發編程之線程第一篇

 

3.4 原理之線程運行

Java虛擬機棧
JVM中由堆、棧、方法區所組成,其中棧記憶體是給線程使用,每個線程啟動後,虛擬機就會為其分配一塊棧記憶體。

  • 每個棧由多個棧幀(Frame)組成,對應著每次方法調用時所占用的記憶體
  • 每個線程只能有一個活動棧幀,對應著當前正在執行的那個方法

線程上下文切換(Thread Context Switch)

因為以下一些原因導致cpu不再執行當前的線程,轉而執行另一個線程的代碼

  • 線程的cpu時間片用完
  • 垃圾回收
  • 有更高優先順序的線程需要運行
  • 線程自己調用了sleep、yield、join、park、synchronized、lock等方法程式
    當Context Switch發生時,需要由操作系統保存當前線程的狀態,並恢復另一個線程的狀態,Java中對應的概念就是程式計數器(Program Counter Register),它的作用是記住下一條jvm指令的執行地址,是線程私有的
    在這裡插入圖片描述

3.5 常見方法

方法名功能說明註意
start() 啟動一個新線程,在新的線程運行run方法中的代碼 start方法只是讓線程進入就緒,裡面的代碼不一定立刻運行(CPU的時間片還沒分給它)。每個線程對象的start方法只能調用一次,如果調用了多次會出現IllegalThreadStateException
run() 新線程啟動後會調用的方法 如果在構造Thread對象時傳遞了Runnable參數,則線程啟動後調用Runnable中的run 方法,否則預設不執行任何操作。但可以創建Thread的子類對象,來覆蓋預設行為
join() 等待線程運行結束  
join(long n) 等待線程運行結束,最多等待n毫秒  
getId() 獲取線程長整型的id id唯一
getName() 獲取線程名  
setName(String) 修改線程名  
getPriority() 獲取線程優先順序  
setPriority(int) 修改線程優先順序 java中規定線程優先順序是1~10的整數,較大的優先順序能提高該線程被CPU調度的機率
getState() 獲取線程狀態 java中線程狀態是用6個enum表示,分別為 :NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED
isInterrupted() 判定是否被打斷 不會清除打斷標記
isAlive() 線程是否存活(還沒有運行完畢)  
interrupt() 打斷線程 如果被打斷線程正在sleep,wait,join會導致被打斷的線程拋出InterruptedException,並清除打斷標記;如果打斷的正在運行的線程,則會設置打斷標記;park的線程被打斷,也會設置打斷標記
interrupted() static 判定當前線程是否被打斷 會清除打斷標記
currendThread(0 static 獲取當前正在執行的線程  
sleep(long n) static 讓當前執行的線程休眠n毫秒,休眠時讓出cpu的時間片給其他線程  
yieId() static 提示線程調度器讓出當前線程對CPU的使用 主要是為了測試和調試

3.6 start與run

調用run
在這裡插入圖片描述
輸出
在這裡插入圖片描述
程式仍在main線程運行,FileReader.read()方法調用還是同步的

3.7 sleep與yield

sleep

  1. 調用sleep會讓當前線程從Running進入Timed Waiting狀態
  2. 其它線程可以使用 interrupt方法打斷正在睡眠的線程,這時sleep方法會拋出InterruptedException
  3. 睡眠結束後的線程未必會立刻得到執行
  4. 建議用TimeUnit的sleep代替Thread的sleep來獲得更好地可讀性
    yield
    1、 調用yield會讓當前線程從Running進入Runnable狀態,然後調度執行其它同優先順序的線程。如果這時沒有同優先順序的線程,那麼不能保證讓當前線程暫停的效果
    2、具體的實現依賴於操作系統的任務調度器
    線程優先順序
  • 線程優先順序會提示(hint)調度器優先調度該線程,但它僅僅是一個提示,調度器可以忽略它
  • 如果cpu比較忙,那麼優先順序高的線程會獲得更多的時間片,但cpu閑時,優先順序幾乎沒作用
    在這裡插入圖片描述

案例 - 防止CPU占用100%

sleep實現
在沒有利用cpu來計算時,不要讓while(true)空轉浪費cpu,這時可以使用yield或sleep來讓出cpu的使用權給其他程式
在這裡插入圖片描述

  • 可以用wait或條件變數達到類似的效果
  • 不同的是,後兩種都需要加鎖,並且需要相應的喚醒操作,一般適用於要進行同步的場景
  • sleep適用於無需鎖同步的場景

3.8 join方法詳解

為什麼需要join
下麵的代碼執行,列印r是什麼?
在這裡插入圖片描述
分析

  • 因為主線程和線程t1是並行執行的,t1線程需要1秒之後才能算出r=10
  • 而主線程一開始就要列印r的結果,所以只能列印出r=0
    解決方法
  • 用sleep行不行?為什麼?
  • 用join,加在start之後即可
    在這裡插入圖片描述
    應用之同步 (案例1)
    以調用方角度來講,如果
  • 需要等待結果返回,才能繼續運行就是同步
  • 不需要等待結果返回,就能繼續運行就是非同步
    在這裡插入圖片描述
    在這裡插入圖片描述
    在這裡插入圖片描述
    在這裡插入圖片描述
    有時效的join
    等夠時間
    在這裡插入圖片描述
    輸出
    在這裡插入圖片描述

3.9 interrupt方法詳解

打斷sleep,wait,join的線程
打斷sleep的線程,會清空打斷狀態,以sleep為例
在這裡插入圖片描述
輸出
在這裡插入圖片描述
打斷正常運行的線程
打斷正常運行的線程,不會清空打斷狀態
在這裡插入圖片描述
輸出
在這裡插入圖片描述

兩階段終止模式

Two Phase Termination
在一個線程T1中如何“優雅”終止線程T2?這裡的【優雅】指的是給T2一個料理後事的機會。
1、 錯誤思路

  • 使用線程對象的stop()停止線程
    (1)stop方法會真正殺死線程,如果這時線程鎖住了共用資源,那麼當它被殺死後就再也沒有機會釋放鎖,其它線程將永遠無法獲取鎖。
  • 使用System.exit(int)方法停止線程
    (1)目的僅是停止一個線程,但這種做法會讓整個程式都停止
    在這裡插入圖片描述
package com.example.demo;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TwoPhaseTermination {
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        test.start();

        Thread.sleep(3500);
        test.stop();
    }
}

@Slf4j
class Test{
    private Thread monitor;

    /**
     * 啟動監控線程
     */
    public void start() {
        monitor = new Thread(() -> {
            while (true) {
                Thread currentThread = Thread.currentThread();
                if (currentThread.isInterrupted()) {
                    log.info("料理後事");
                    break;
                }
                try {
                    // 情況1
                    Thread.sleep(1000);
                    log.info("執行監控記錄");
                } catch (Exception e) {
                	// 因為sleep出現異常後,會消除打斷標記
                    // 需要重置打斷標記
                    e.printStackTrace();
                    currentThread.interrupt();
                }
            }
        });
        monitor.start();
    }

    /**
     * 停止監控線程
     */
    public void stop() {
        monitor.interrupt();
    }

}

在這裡插入圖片描述
打斷park線程
打斷park線程,不會清空打斷狀態
在這裡插入圖片描述
輸出
在這裡插入圖片描述
如果打斷標記已經是true,則park會失效,可以如下操作 :
在這裡插入圖片描述

3.10 不推薦的方法

還有一些不推薦使用的方法,這些方法已過時,容易破壞同步代碼塊,造成線程死鎖。

方法名static功能說明
stop()   停止線程運行
suspend()   掛起(暫停)線程運行
resume()   恢複線程運行

3.11 主線程與守護線程

預設情況下,Java進程需要等待所有線程都運行結束,才會結束。有一種特殊的線程叫做守護線程,只要其它非守護線程運行結束了,即使守護線程的代碼沒有執行完,也會強制結束。
在這裡插入圖片描述
在這裡插入圖片描述
註意

  • 垃圾回收器線程就是一種守護線程
  • Tomcat中的Acceptor和Poller線程都是守護線程,所以Tomcat接收到shutdown命令後,不會等待它們處理完當前請求。

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

-Advertisement-
Play Games
更多相關文章
  • Tensorflow機器學習入門——網路可視化TensorBoard ...
  • *Preparation 1. Black's Futures Option Model (1) Key parameter: S = F, b = 0; (2) Task: Need an option for an asset (futures price = 120), assume K = ...
  • 一、前言 近期疫情嚴重,身為社畜的我只能在家中繼續鑽研技術了。之前寫過一篇關於搭建FIFO驗證平臺的博文,利用SV的OOP特性對FIFO進行初步驗證,但有很多不足之處,比如結構不夠規範、驗證組件類不獨立於DUT等問題。此次嘗試驗證更複雜的IP,並利用SV的更多高級特性來搭建層次化驗證平臺。 二、AP ...
  • JavaSE學習筆記(5) 內部類和String類 一.內部類基礎 轉自 "菜鳥教程" ​ 在 Java 中,可以將一個類定義在另一個類裡面或者一個方法裡面,這樣的類稱為內部類。廣泛意義上的內部類一般來說包括這四種:成員內部類、局部內部類、匿名內部類和靜態內部類。下麵就先來瞭解一下這四種內部類的用法 ...
  • Tensorflow機器學習入門,捲積神經網路識別MINIST手寫體數據集 ...
  • JavaSE學習筆記(4) 抽象類和介面 抽象方法和抽象類 ·抽象方法 使用abstract修飾的方法,沒有方法體,只有聲明。定義的是一種“規範”,就是告訴子類必須要給抽象方法提供具體的實現。 特點 1. 抽象方法必須聲明在抽象類中。 2. 抽象方法聲明引入了一個新方法,但不提供該方法的實現,由於抽 ...
  • Lamda表達式學習筆記二 lamda表達式 方法引用 上一篇講到Lamda體就是對函數式介面方法的實現 ,在方法體中我們可能會引用其他方法實現邏輯,所以在lamda體中我們可以直接引用器方法 I 對象::實例方法名 /** * 對象::實例方法名 */ @Test public void test ...
  • 註意:可變參數類型是在jdk1.5版本的新特性,數組類型是jdk1.0就有了。 這篇文章主要介紹了Java方法的可變參數類型,通過實例對Java中的可變參數類型進行了較為深入的分析,需要的朋友可以參考下。 Java方法中的可變參數類型是一個非常重要的概念,有著非常廣泛的應用。本文就以實例形式對此加以 ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...