[英]In java in which thread is an object runned
我不明白為什么一個線程能夠調用一個 object 的方法,該方法在另一個處於 while true 循環中的線程中被刪除。 根據我的基本知識,如果線程處於 while true 循環中,您將無法與其交互,因此即使在該線程中刪除了 object。
謝謝你的建議。
這是主要的 class
/**
* main
*/
public class mainClass {
public static void main(String[] args) {
whatTime wt = new whatTime();
threadA ta = new threadA(wt);
ta.start();
while (true) {
}
}
}
這是線程 A class
/**
* threadA
*/
public class threadA extends Thread {
private whatTime wt;
public threadA(whatTime wt) {
System.out.println("threadA() constructor");
this.wt = wt;
}
public void run() {
while (true) {
//every 10s
try {
Thread.sleep(10000);
System.out.println("threadA: " + wt.getTime());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
這是我使用的 object
public class whatTime {
public whatTime() {
System.out.println("whatTime() constructor");
}
public long getTime() {
return System.currentTimeMillis();
}
}
我不明白為什么一個線程能夠調用在另一個線程中刪除的 object 的方法
我想你誤會了。
whatTime wt = new whatTime();
這做了兩件完全不同的事情。
在本地空間(在堆棧中,事實上,不能與其他線程交互,無論如何 - 該方法返回后立即發生的任何事情都會立即重用該堆棧,所以如果任何其他線程敢於嘗試,你就是麻煩大了)——它聲明了一個引用變量。 這通常是一個 64 位值,它告訴 JVM 在堆中的何處找到 object。 它不是存儲在wt
中的 whatTime 本身的實例! - 它只是對它的引用。 想想:“地址簿中的一頁,上面有房子的地址”,而不是:“房子”。 whatTime
的一個實例是房子, wt
是地址簿頁面。
其他線程無法與地址簿頁面進行交互 - 充其量,您可以復制它,然后將副本交給另一個線程。 但他們可以拿走地址簿頁面的副本,開車過去,通過 window 扔磚頭。如果那天晚些時候你也開車過去,你會看到損壞的 window。
new WhatTime()
- “建造”房子,並將房子的地址分配給wt
變量。
threadA ta = new threadA(wt);
這創建了一個新的threadA
實例(並將它的地址記錄到局部變量空間),並將地址簿頁面的副本交給它(java 始終是傳遞副本,但wt
是參考,而不是 object 本身)。 您現在有一個地址簿頁面,上面有“123 Fairfield lane”, threadA
也是如此 - threadA 有自己的私有副本。 threadA對該地址簿頁面所做的任何操作都不會影響您的地址簿。 但是,如果他們決定撥打 go 到該地址的房子 - 您也可以看到。
根據我的基本知識,如果線程處於 while true 循環中,則您無法與之交互
在你說“在一段時間內真正循環”的地方,你真正的意思是“如果它正在運行”。 它是否在循環並不重要,重要的是它在運行。
這指的是 memory model。現代 CPU根本無法讀取或寫入 memory。 因為 memory 總線太遠了,那些電子以大約 60% 的光速或類似的速度穿過主板上的線路,這意味着它就像相對於 CPU 的慢糖蜜,這就是為什么他們不能這樣做的原因。 相反,有一小塊非常快的 memory 直接在核心上,分成幾個具有設定大小的“頁面”(大約 64k - 取決於你的處理器),CPU 唯一能做的就是告訴memory controller: Go 將此頁寫回到主 memory(全部 64k)的這個地址,然后將其擦除並替換為從 A 到 B 的主 memory 庫的內容。我會等待。 它的所有 1000 個周期,因為,男孩,我需要等待很長時間才能讓你做這些事情,因為 memory 銀行太遠了。
鑒於這就是 CPU 的工作方式,java 需要“聲明”一些權利,因為如果不這樣做,它將運行得非常慢。 它聲稱的權利之一是重新排序和本地緩存。
JVM可能會將您從堆中獲得的任何內容(因此任何非本地)緩存在 CPU 內核的片上緩存中。 這意味着如果您有 1 個字段,並且有 2 個線程同時讀取和寫入該字段,則每個線程實際上可能只是寫入其本地副本,而 JVM 並沒有具體說明如何將其合並回 memory -通常,一些任意頁面寫入“獲勝”並且都是不可靠的。
換句話說,如果 2 個線程正在訪問同一個堆(同一個字段),您的代碼就會被破壞:取決於 CPU、體系結構、在您執行操作時音樂播放器上播放的音樂,或月相- 你得到一個或另一個結果。 哎喲。
為了首先使線程化成為可能,JMM(Java Memory 模型)定義了某些特定的操作來建立“Happens-Before”。 如果根據 JMM 動作 A“發生在”動作 B 之前,那么 B 行及其后的任何行將無法觀察到 state,就像 A 運行之前那樣。 本質上,如果 A“發生在(根據 JMM)”B 之前,那么 A 確實發生在 B 之前,除非B 不可能觀察到這一點,在這種情況下,JVM 仍然可以按照它想要的任何順序自由運行這些事情.
建立 HB 可以通過synchronized
, volatile
,各種特殊動作來完成( thread.start()
是 HB 相對於該線程內的第一行,例如),當然也可以通過使用(核心)庫函數來完成這些事情,例如ConcurrentHashMap
和 juc package 中的許多其他東西。
這大概是您閱讀和誤解的內容:您不應該交互(讀取或寫入)另一個已經啟動但尚未死亡但也與之交互的線程的任何字段,除非您仔細管理此訪問並確保所有可能的交互可能受到 HB/HA 關系的保護,因此您的代碼每次都會以相同的方式運行,並且不會變成一些瘋狂的邪惡硬幣翻轉游戲,它取決於眾所周知的蝴蝶的翅膀。 如果您違反規則,並且您從 2 個單獨的線程與同一字段交互而沒有建立 HB/HA——那么“它有效”(沒有直接崩潰,沒有拋出異常,代碼編譯正常,並且會運行)——但是結果或多或少是任意的:
class Example {
int x;
void crazy() {
x = 1;
new Thread(() -> x = 5).start();
new Thread(() -> x = 10).start();
System.out.println(x);
}
}
上面的代碼可以合法地打印 1、5或10。JVM 工作正常並且滿足規范,無論它恰好返回哪一個- 並且 JVM 也不保證它是隨機的(始終返回的 JVM 1 在這里,很好。一個 JVM 總是返回 1 除了它在第 3 個月的第 5 個星期一返回 10 - 很好)。 這樣寫的代碼就是問題所在。 沒有辦法知道,真的。 基本上不可能對其進行測試(假設始終返回 1 的 JVM 在這里也很好)。 您可以運行它的絕大多數方式(JVM 供應商、版本、操作系統、體系結構等的組合)將打印“1”,但“10”不會不正確。
在此特定代碼中,完全沒有問題 - wt
引用被寫入一次,這是相對於線程的 HB(因為它先於t.start()
),因此訪問引用不是問題,因為它永遠不會改變,並且System.currentTimeMillis()
不訪問任何字段,因此對.getTime()
的調用也沒有問題。
因此,這段代碼有效……就好了。 沒有什么問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.