當前位置:首頁 » 存儲配置 » java線程局部存儲

java線程局部存儲

發布時間: 2023-04-30 18:21:53

java 基本數據類型作為局部變數存儲在哪

基本數據類型作為局部變數是放在棧中的,new出來的對象是放在堆中的,用static聲明的變數是靜態變數,靜態變數和字元串常量是放在data segment中的

❷ 使用線程池時一定要注意的五個點

很多場景下應用程序必須能夠處理一系列傳入請求,簡單的處理方式是通過一個線程順序的處理這些請求,如下圖:

單線程策略的優勢和劣勢都非常明顯:

優勢:設計和實現簡單;劣勢:這種方式會帶來處理效率的問題,單線程的處理能力是有限,不能發揮多核處理器優勢。

在這種場景下我們就需要考慮並發,一個簡單的並發策略就是Thread-Per-Message模式,即為每個請求使用一個新的線程。

Thread-Per-Message策略的優勢和劣勢也非常明顯:

優勢:設計和實現比較簡單,能夠同時處理多個請求,提升響應效率;

劣勢:主要在兩個方面

1.資源消耗 引入了在串列執行中所沒有的開銷,包括線程創建和調度,任務處理,資源分配和回收以及頻繁上下文切換所需的時間和資源。2.安全

有沒有一種方式可以並發執行又可以克服Thread-Per-Message的問題?

採用線程池的策略,線程池通過控制並發執行的工作線程的最大數量來解決Thread-Per-Message帶來的問題。可見下圖,請求來臨時先放入線程池的隊列

線程池可以接受一個Runnable或Callable<T>任務,並將其存儲在臨時隊列中,當有空閑線程時可以從隊列中拿到一個任務並執行。

反例(使用 Thread-Per-Message 策略)

正例(使用 線程池 策略)

JAVA 中(JDK 1.5+)線程池的種類:

程序不能使用來自有界線程池的線程來執行依賴於線程池中其他任務的任務。

有兩個場景:

要緩解上面兩個場景產生的問題有兩個簡單的辦法:

真正解決此類方法還是需要梳理線程池執行業務流程,不要在有界線程池中執行相互依賴的任務,防止出現競爭和死鎖。

向線程池提交的任務需要支持中斷。從而保證線程可以中斷,線程池可以關閉。線程池支持 java.util.concurrent.ExecutorService.shutdownNow() 方法,該方法嘗試停止所有正在執行的任務,停止等待任務的處理,並返回等待執行的任務的列表。

但是 shutdownNow() 除了盡力嘗試停止處理主動執行的任務之外不能保證一定能夠停止。例如,典型的實現是通過Thread.interrupt()來停止,因此任何未能響應中斷的任務可能永遠不會終止,也就造成線程池無法真正的關閉。

反例:

正例:

線程池中的所有任務必須提供機制,如果它們異常終止,則需要通知應用程序.

如果不這樣做不會導致資源泄漏,但由於池中的線程仍然被會重復使用,使故障診斷非常困難或不可能。

在應用程序級別處理異常的最好方法是使用異常處理。異常處理可以執行診斷操作,清理和關閉Java虛擬機,或者只是記錄故障的詳細信息。

也就是說在線程池裡執行的任務也需要能夠拋出異常並被捕獲處理。

任務恢復或清除操作可以通過重寫 java.util.concurrent.ThreadPoolExecutor 類的 afterExecute() 鉤子來執行。

當任務通過執行其 run() 方法中的所有語句並且成功結束任務,或者由於異常而導致任務停止時,將調用此鉤子。

可以通過自定義 ThreadPoolExecutor 服務來重載 afterExecute()鉤子。

還可以通過重載 terminated() 方法來釋放線程池獲取的資源,就像一個finally塊。

反例:

任務意外終止時作為一個運行時異常,無法通知應用程序。此外,它缺乏恢復機制。因此,如果Task拋出一個NullPointerException ,異常將被忽略。

正例:

另外一種方式是使用 ExecutorService.submit() 方法(代替 execute() 方法)將任務提交到線程池並獲取 Future 對象。

當通過 ExecutorService.submit() 提交任務時,拋出的異常並未到達未捕獲的異常處理機制,因為拋出的異常被認為是返回狀態的一部分,因此被包裝在ExecutionException ,並由Future.get() 返回。

java.lang.ThreadLocal 類提供線程內的本地變數。根據Java API

ThreadLocal對象需要關注那些對象被線程池中的多個線程執行的類。

線程池緩存技術允許線程重用以減少線程創建開銷,或者當創建無限數量的線程時可以降低系統的可靠性。

當 ThreadLocal 對象在一個線程中被修改,隨後變得可重用時,在重用的線程上執行的下一個任務將能看到該線程上執行過的上一個任務修改的ThreadLocal 對象的狀態。

所以要在使用線程池時重新初始化的ThreadLocal對象實例。

反例:

DiaryPool類創建了一個線程池,它可以通過一個共享的無界的隊列來重用固定數量的線程。

在任何時候,不超過numOfThreads個線程正在處理任務。如果在所有線程都處於活動狀態時提交其他任務,則 它們在隊列中等待,直到線程可用。

當線程循環時,線程的線程局部狀態仍然存在。

下表顯示了可能的執行順序:

時間任務線程池提交方法日期1t11doSomething1()星期五2t22doSomething2()星期一3t31doSomething3()星期五

在這個執行順序中,期望從doSomething2() 開始的兩個任務( t 2和t 3 doSomething2() 將當天視為星 期一。然而,因為池線程1被重用,所以t 3觀察到星期五。

解決方案(try-finally條款)

符合規則的方案removeDay() 方法添加到Diary類,並在try‐finally 塊中的實現doSomething1() 類的doSomething1() 方法的語句。finally 塊通過刪除當前線程中的值來恢復threadlocal類型的days對象的初始狀態。

如果threadlocal變數再次被同一個線程讀取,它將使用initialValue()方法重新初始化 ,除非任務已經明確設置了變數的值。這個解決方案將維護的責任轉移到客戶端( DiaryPool ),但是當Diary類不能被修改時是一個好的選擇。

解決方案(beforeExecute())

使用一個自定義ThreadPoolExecutor 來擴展 ThreadPoolExecutor並覆蓋beforeExecute() 方法。beforeExecute() 方法在Runnable 任務在指定線程中執行之前被調用。該方法在線程 「t」 執行任務 「r」 之前重新初始化 threadlocal 變數。

❸ java jvm 的內存到底是什麼分配的,看得有點糊塗。

一個完整的Java程序運行過程會涉及以下內存區域:

l寄存器:JVM內部虛擬寄存器,存取速度非常快,程序不可控制。

l棧:保存局部變數的值,包括:1.用來保存基本數據類型的值;2.保存類的實例,即堆區對象的引用(指針)。也可以用來保存載入方法時的幀。

l堆:用來存放動態產生的數據,手敗比如new出來的對象。注意創建出來的對象只包含屬於各自的成員變數,並不包括成員方法。因為同一個類的對象擁有各自的成員變數,存儲在各自的堆中,但是他們共享該類的方法,並不是每創建一個對象就把成員方法復制一次。

模薯猜l常量池:JVM為每個已載入的類型維護一個常量池,常量池就是這個類型用到的常量的一個有序集合。包括直接常量(基本類型,String)和對其他類型、方法、欄位的符號引用(1)。池中的數據和數組一樣通過索引訪問。由於常量池包含了一個類型所有的對其他類型、方法、欄位的符號引用,所以常量池在Java的動態鏈接中起了核心作用。常量池存在於旦型堆中。

l代碼段:用來存放從硬碟上讀取的源程序代碼。

l數據段:用來存放static定義的靜態成員。

❹ java里找變數來源

一、局部變數存儲在方法棧中

在方法中聲明的變數,即該變數是局部變數,每當程序調用方法時,系統都會為該方法建立一個方法棧,其所在方法中聲明的變數就放在方法棧中,當方法結束系統會釋放方法棧,其盯判對應在該方法中聲明的變數隨凱察改著棧的銷毀而結束,這就局部變數只能在方法中有效的原因。在方法中聲明的變數可以是基本類型的變數,也可以是引用類型的變數。

(1)當聲明是基本類型的變數的時,其變數名及值(變數名及值是兩個概念)是放在JAVA虛擬機棧中

(2)當聲明的是引用變數時,所聲明的變數(該變數實際上是在方法中存儲的是內存地址值)是放在JAVA虛擬機的棧中,該變數所指向的對象是放在堆類存中的。

二、全局變數存儲在堆中

在類中聲明的變數是成員變數,也叫全局變數,放在堆中的(因為全局變數不會隨著某個方法執行結束而銷毀)。同樣在類中聲明的變數即可是基本類型的變數 也可是引用類型的變數

(1)當聲明的是基本類型的變數其變數名及其值放在沒虧堆內存中的

(2)引用類型時,其聲明的變數仍然會存儲一個內存地址值,該內存地址值指向所引用的對象。引用變數名和對應的對象仍然存儲在相應的堆中

❺ 主內存與java內存區域(堆,方法區)有什麼區別

這兩天看了一下深入淺出JVM這本書,推薦給高級的java程序員去看,對你了解JAVA的底層和運行機制有
比較大的幫助。
廢話不想講了.入主題:
先了解具體的概念:
JAVA的JVM的內存可分為3個區:堆(heap)、棧(stack)和方法區(method)
堆區:
1.存儲的全部是對象,每個對象都包含一個與之對應的class的信息。(class的目的是得到操作指令)
2.jvm只有一個堆區(heap)被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身
棧區:
1.每個線程包含一個棧區,棧中只保存基礎數據類型的對象和自定義對象的引用(不是對象),對象都存放在堆區中
2.每個棧中的數據(原始類型和對象引用)都是私有的,其他棧不能訪問。
3.棧分為3個部分:基本類型變數區、執行環境上下文、操作指令區(存放操作指令)。
方法區:
1.又叫靜態區,跟堆一樣,被所有的線程共享。方法區包含所有的class和static變數。
2.方法區中包含的都是在整個程序中永遠唯一的元素,如class,static變數。
為了更清楚地搞明白發生在運行時數據區里的黑幕,我們來准備2個小道具(2個非常簡單的小程序)。
AppMain.java
public class AppMain
//運行時, jvm 把appmain的信息都放入方法區
{
public static void main(String[] args) //main 方法本身放入方法區。
{
Sample test1 = new Sample( " 測試1 " ); //test1是引用,所以放到棧區里, Sample是自定義對象應該放到堆裡面
Sample test2 = new Sample( " 測試2 " );
test1.printName();
test2.printName();
}
}
Sample.java
public class Sample //運行時, jvm 把appmain的信息都放入方法區
{
/** 範例名稱 */
private name; //new Sample實例後, name 引用放入棧區里, name 對象放入堆里
/** 構造方法 */
public Sample(String name)
{
this .name = name;
}
/** 輸出 */
public void printName() //print方法本身放入 方法區里。
{
System.out.println(name);
}
}
OK,讓我們開始行動吧,出發指令就是:「java AppMain」,包包里帶好我們的行動向導圖,Let』s GO!

系統收到了我們發出的指令,啟動了一個Java虛擬機進程,這個進程首先從classpath中找到AppMain.class文件,讀取這個文件中的二進制數據,然後把Appmain類的類信息存放到運行時數據區的方法區中。這一過程稱為AppMain類的載入過程。
接著,Java虛擬機定位到方法區中AppMain類的Main()方法的位元組碼,開始執行它的指令。這個main()方法的第一條語句就是:
Sample test1=new Sample("測試1");
語句很簡單啦,就是讓java虛擬機創建一個Sample實例,並且呢,使引用變數test1引用這個實例。貌似小case一樁哦,就讓我們來跟蹤一下Java虛擬機,看看它究竟是怎麼來執行這個任務的:
1、 Java虛擬機一看,不就是建立一個Sample實例嗎,簡單,於是就直奔方法區而去,先找到Sample類的類型信息再說。結果呢,嘿嘿,沒找到@@,這會兒的方法區里還沒有Sample類呢。可Java虛擬機也不是一根筋的笨蛋,於是,它發揚「自己動手,豐衣足食」的作風,立馬載入了Sample類,把Sample類的類型信息存放在方法區里。
2、 好啦,資料找到了,下面就開始幹活啦。Java虛擬機做的第一件事情就是在堆區中為一個新的Sample實例分配內存, 這個Sample實例持有著指向方法區的Sample類的類型信息的引用。這里所說的引用,實際上指的是Sample類的類型信息在方法區中的內存地址,其實,就是有點類似於c語言里的指針啦~~,而這個地址呢,就存放了在Sample實例的數據區里。
3、 在JAVA虛擬機進程中,每個線程都會擁有一個方法調用棧,用來跟蹤線程運行中一系列的方法調用過程,棧中的每一個元素就被稱為棧幀,每當線程調用一個方法的時候就會向方法棧壓入一個新幀。這里的幀用來存儲方法的參數、局部變數和運算過程中的臨時數據。OK,原理講完了,就讓我們來繼續我們的跟蹤行動!位於「=」前的Test1是一個在main()方法中定義的變數,可見,它是一個局部變數,因此,它被會添加到了執行main()方法的主線程的JAVA方法調用棧中。而「=」將把這個test1變數指向堆區中的Sample實例,也就是說,它持有指向Sample實例的引用。
OK,到這里為止呢,JAVA虛擬機就完成了這個簡單語句的執行任務。參考我們的行動向導圖,我們終於初步摸清了JAVA虛擬機的一點點底細了,COOL!
接下來,JAVA虛擬機將繼續執行後續指令,在堆區里繼續創建另一個Sample實例,然後依次執行它們的printName()方法。當JAVA虛擬機執行test1.printName()方法時,JAVA虛擬機根據局部變數test1持有的引用,定位到堆區中的Sample實例,再根據Sample實例持有的引用,定位到方法去中Sample類的類型信息,從而獲得printName()方法的位元組碼,接著執行printName()方法包含的指令。

❻ Java數組是存儲在內存中的什麼地方

因為我也不清楚你理解的內存指啥,說我的理解,猜測一下,但願符合你的理解:
對程序而言,內存分為:
a、靜態區塊或叫全局區塊,指程序載入的時候就分配的固定存儲區;
b、棧內存,代碼塊執行的時候動態分配的內存塊,代碼運行完後,操作系統或運行平台負責自動回收這部分使用的內存。
c、堆內存塊,由操作系統提供api程序自由分配管理的內存塊。
java的內存管理機制,我個人認為是黑盒的,寫java也很少有人去關心,但是如果看虛擬機的實現源碼還是能知道的。(樓主懂c由興趣自己看)。那麼從逆向工程的角度我們可以猜測到:
靜態類,靜態變數,存儲在,靜態區塊中。
類本身代碼需要時再載入到內存中,java的動態代理實現的根本。既然是動態載入肯定是在堆中,不過載入進來的類代碼可能永不會銷毀(虛擬機運行區間內,可以加快運行平台的處理速度,java屬於半編譯半解釋的一門語言,要解釋執行的是預先編譯好的指令,就是class類文件)。
我們創建的任何對象,時間是隨機的,創建的時候才會分配內存,必然在堆上。虛擬機的垃圾回收機制要管理的剛好是這部分內存。
類中的方法,或靜態代碼塊必定運行在占上,代碼運行完,內存立即回收。此處有個例外,不同於c語言或操作系統實現。在方法中返回的任何對象:c需要動態分配存儲空間,兵自行管理,或者申明為全局局部靜態存儲,代碼運行完,對象才能保留下來。java可以直接保留下來。說明虛擬機自動給我們申請了動態內存保存對象。此處分配的內存也需要垃圾回收機制管理。沒門語言都有原始類型,方法或函數中返回的原始類型不需要從堆上分配內存,直接復制給接收變數,或代碼運行的形參所分配的空間中即可。
從操作系統理解,內存分為內核使用的內存與程序使用的內存,java運行在虛擬機上,虛擬機運行在操作系統上,內核內存屬於操作系統自用內存,由操作系統負責管理,運用程序不能直接管理分配內核內存。應用程序可管理的是分配給自己的運行空間。因此java使用內存屬於應用程序內存。內核內存管理方式windows與unix實現管理方式不一樣,最大差別windows的每一個程序,內核需要使用掉一部分地址空間,餘下的留給應用程序。如32位系統,總共可以使用的地址空間是4G內核要用掉其中1G或者2G。但是unix實現的系統應用程序可以直接使用4G地址空間。有興趣請參考操作系統內核相關書籍。

熱點內容
騰訊雲伺服器購買網址 發布:2025-02-11 21:37:46 瀏覽:60
安卓電話視頻怎麼投電視上 發布:2025-02-11 21:32:27 瀏覽:18
易簽到源碼 發布:2025-02-11 21:31:03 瀏覽:498
編程班會 發布:2025-02-11 21:27:19 瀏覽:738
ubuntu編譯fortran 發布:2025-02-11 21:21:59 瀏覽:201
雲伺服器寬頻單位 發布:2025-02-11 20:48:11 瀏覽:538
安卓數據線公頭是哪個 發布:2025-02-11 20:45:42 瀏覽:812
網址原始密碼是什麼 發布:2025-02-11 20:33:52 瀏覽:72
怎麼創建伺服器我的世界網易 發布:2025-02-11 20:18:36 瀏覽:467
伺服器電腦與客戶端的連接 發布:2025-02-11 20:18:32 瀏覽:36