日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】JAVA 并发性和多线程 -- 读感 (二 线程间通讯,共享内存的机制)

發布時間:2025/5/22 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】JAVA 并发性和多线程 -- 读感 (二 线程间通讯,共享内存的机制) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文地址:https://www.cnblogs.com/edenpans/p/6020113.html

?

參考文章:http://ifeve.com/java-concurrency-thread-directory/?

其中的競態,線程安全,內存模型,線程間的通信,java ThreadLocal類小節部分內容。

  • 1.目錄略覽
線程的基本概念:介紹線程的優點,代價,并發編程的模型。如何創建運行java 線程。 線程間通訊,共享內存的機制:競態條件與臨界區,線程安全和共享資源與不可變性。java內存模型,線程間的通信,java ThreadLocal類,線程信號 死鎖相關,資源競爭相關:死鎖,如何避免死鎖,饑餓和公平,嵌套管程鎖死,Slipped conditions(從一個線程檢查某一特定條件到該線程操作此條件期間,這個條件已經被其它線程改變,導致第一個線程在該條件上執行了錯誤的操作),鎖,讀鎖和寫鎖,重入寫死,信號量,阻塞隊列,線程池,CAS(compare and swap 理論),同步器,無阻塞算法,阿姆達爾定律(計算處理器平行運算之后效率提升的能力)。?
  • 2.競態條件與臨界區
當多個線程訪問了相同的資源,并且對這些資源做了寫操作的時候,是不安全的。資源可以代表:同一內存區(變量,數組或者對象),系統(數據庫,web services)或文件。 對于一個簡單的加法操作 ? ? this.count = this.count + value,JVM執行指令的順序應該是: 從內存獲取 this.count 值放到寄存器 將寄存器的值添加value ? ? 將寄存器的值寫會內存 如果兩個線程 交叉執行,一個線程加2 一個線程加3,可能最后的結果不是5,而是2 或者3. 競態條件:兩個線程競爭同一個資源時,如果對資源訪問順序敏感,就存在競態條件。 臨界區:導致競態條件發生的代碼區成為臨界區。 在臨界區適當的同步可以避免競態條件。??
  • 3.線程安全與共享資源
允許被多個線程同時執行的代碼稱為線程安全的代碼。線程安全的代碼不包含競態條件。 1.局部變量 1.1局部基本類型變量 是存儲在線程自己的棧中的,所以基礎類型的局部變量是線程安全的。 1.2.局部對象引用 引用所指向的對象沒有存儲到線程的棧內。所有的對象都在共享堆中。 兩段代碼,不管是基礎類型還是引用對象,它們都是局部變量,由于都沒有被其他線程獲取,是線程安全的。 public void someMethod(){long threadSafeInt = 0;threadSafeInt++; } public void someMethod(){LocalObject localObject = new LocalObject();localObject.callMethod();method2(localObject); }public void method2(LocalObject localObject){localObject.setValue("value"); } 2.對象成員 對象成員是存儲在堆上。如果兩個線程同時更新同一個對象的同一成員,這個代碼就是線程不安全的。 public class NotThreadSage{StringBuilder builder = New StringBuilder();public add(String text) {this.builder.append(text);} }

? ? ? 線程控制逃逸判斷

? ? ? ? ? ?一個資源的創建,使用銷毀都在同一個線程內完成,且永遠不會脫離該線程的控制。

即使對象本身線程安全,但是該對象中包含的其他的資源,也許整體的應用不是線程安全的。 3.線程安全及不可變性 immutable 和 read only 的差別:當一個變量是只讀的時候,變量的值不可改變,但是可以在其他變量發生改變的時候發生改變。而不變 是不會改變的。
  • ? ? ?4.java 內存模型
java內存模型規范了java虛擬機與計算機內存如何協同工作的。

?

每個java虛擬機的線程都擁有自己的線程棧,包括了這個線程調用的方法當前執行點的相關信息。一個線程只能訪問自己的線程棧。本地變量只對當前線程可見。

?

對象是放在堆上。 每個線程都有自己的線程棧,如果是基本類型的變量,直接存放在線程棧中,如果是對象的引用,那么引用地址會放在線程棧中,而對象會在堆中,這樣有可能存在兩個線程同時引用相同的對象。 public class MyRunnable implements Runnable() {public void run() {methodOne();}public void methodOne() {int localVariable1 = 45;MySharedObject localVariable2 =MySharedObject.sharedInstance;//... do more with local variables.methodTwo();}public void methodTwo() {Integer localVariable1 = new Integer(99);//... do more with local variable.} }public class MySharedObject {//static variable pointing to instance of MySharedObjectpublic static final MySharedObject sharedInstance =new MySharedObject();//member variables pointing to two objects on the heappublic Integer object2 = new Integer(22);public Integer object4 = new Integer(44);public long member1 = 12345;public long member1 = 67890; }

?

兩個線程啟動后,Object3就是?MySharedObject,而Object2,Object4 是?? ?MySharedObject中的 object2 和 Object4. 現代硬件內存架構

?

Java內存模型和硬件內存架構之間的橋接

?

硬件內存架構中沒有區分線程棧和堆。對于硬件所有線程棧和堆都是分布在主存中。部分線程棧和堆可能出現在CPU緩存和CPU內部的寄存器中。 當對象和變量被存放在計算機不同的內存區域中時,會有一些問題: 1.線程對共享變量修改的可見性。— 兩個線程分布運行在不同的CPU上時,線程的部分變量沒有刷新回主存,此時可能會導致不同步。可以使用 volatile 來避免。 2.當讀,寫和檢查共享變量時出現race conditions。多個線程同時修改共享內存的值,如下圖:

?

可以使用java同步塊,這樣同一時刻只能有一個線程可以進入代碼的臨界區。同步塊還可以保證代碼塊中所有被訪問的變量從主存中讀入,當線程退出同步塊時,所有被更新的變量也會被刷新回主存中,無論該變量是否被聲明為volatile. 5.java 同步塊 java同步塊 (synchronized block) 用來標記方法或者代碼塊是同步的。用來避免競爭。 java同步關鍵字:synchronized 所有其他等待進入該同步塊的線程將被阻塞,直到執行該同步塊的線程退出。 ? ?? 四種不同的同步塊: 實例方法;靜態方法;實例方法中的同步塊;靜態方法中的同步塊。——都是方法上的同步塊。 ? ?? 實例方法同步: public synchronized void add(int value){this.count += value;} 每個實例其方法同步都是同步在不同的對象上。這樣每個實例方法同步都同步在不同的對象上,即該方法所屬的實例,只有一個線程可以在實例方法同步塊中運行。一個實例一個線程。 靜態方法同步: public static synchronized void add(int value){count += value; } 靜態方法同步是指同步在該方法上所在的類對象上的。java虛擬機中一個類只能對應一個類對象,所以同時只允許一個線程執行同一個類中的靜態同步方法。不管類中的哪個靜態同步方法被調用,一個類只能由一個線程同時執行。 ? ? ? ? ? 實例方法中同步塊: public void add(int value){synchronized(this){this.count += value; }}

? ? ? ? ? 示例中使用的this 是代表的調用add方法的實例本身。在同步構造器中用括號括起來的對象叫做監視器對象。

靜態方法中同步塊: public class MyClass {public static synchronized void log1(String msg1, String msg2){log.writeln(msg1);log.writeln(msg2);}public static void log2(String msg1, String msg2){synchronized(MyClass.class){log.writeln(msg1);log.writeln(msg2); }}} 兩個方法不允許同時被線程訪問。 如果第二個同步塊不是同步在MyClass.class這個同步器上,這兩個方法可以同時被線程訪問。 java同步示例: public class Counter{long count = 0;public synchronized void add(long value){this.count += value;}}public class CounterThread extends Thread{protected Counter counter = null;public CounterThread(Counter counter){this.counter = counter;}public void run() {for(int i=0; i<10; i++){counter.add(i);}}}public class Example {public static void main(String[] args){Counter counter = new Counter();Thread threadA = new CounterThread(counter);Thread threadB = new CounterThread(counter);threadA.start();threadB.start(); }}

?

由于兩個線程都是共用一個counter實例,所以add()被調用的時候是同步的,只有一個線程可以調用,另外一個需要等待。 public class Example {public static void main(String[] args){Counter counterA = new Counter();Counter counterB = new Counter();Thread threadA = new CounterThread(counterA);Thread threadB = new CounterThread(counterB);threadA.start();threadB.start(); }} 這個時候兩個線程就可以同時調用add()方法,因為它們分別在不同的實例中。
  • 6.線程通信
線程通信的目的是使線程間可以互相發送信號。 方式: 1.通過共享對象通信 public class MySignal{protected boolean hasDataToProcess = false;public synchronized boolean hasDataToProcess(){return this.hasDataToProcess;}public synchronized void setHasDataToProcess(boolean hasData){this.hasDataToProcess = hasData; }} 兩個線程獲得指向一個MySingal共享實例的引用,以便通信。同時獲取變量的方法設置為同步方法,防止線程不一致。 2.忙等待(Busy Wait) protected MySignal sharedSignal = ......while(!sharedSignal.hasDataToProcess()){//do nothing... busy waiting } 線程B一直在等待數據。但是感覺這里和前面獲取共享變量是一個原理。 3.wait(),notify()和 notifyAll() wait()調用后就處于非運行狀態,直到另外一個線程調用了同一個對象的notify()方法。同時線程必須獲取這個對象的鎖才能調用。 public class MonitorObject{ }public class MyWaitNotify{MonitorObject myMonitorObject = new MonitorObject();public void doWait(){synchronized(myMonitorObject){try{myMonitorObject.wait();} catch(InterruptedException e){...}}}public void doNotify(){synchronized(myMonitorObject){myMonitorObject.notify();}} } 調用這個對象的notify() 的時候,有一個wait的線程會被隨機喚醒,同時也有一個notifyAll()方法來喚醒所有線程。 一旦線程調用了wait()方法,就釋放了所持有的監視器對象上的鎖,就允許了其他線程也可以調用wait()或者notify().同時一個線程被喚醒不是立刻就退出wait()的方法,直到調用notify()的線程退出了自己的同步塊。 4.丟失信號 由于notify()和notifyAll()不會保存調用它們的方法,他們發送的信號如果在wait()之前就有可能丟失,這個時候必須把他們保存在信號類里。 public class MyWaitNotify2{MonitorObject myMonitorObject = new MonitorObject();boolean wasSignalled = false;public void doWait(){synchronized(myMonitorObject){if(!wasSignalled){try{myMonitorObject.wait();} catch(InterruptedException e){...}}//clear signal and continue running.wasSignalled = false;}}public void doNotify(){synchronized(myMonitorObject){wasSignalled = true;myMonitorObject.notify();}} } 應該就是借助一個變量來記錄是否調用過Notify()。 5.假喚醒? 有時由于莫名其妙的原因,線程可能在沒有掉用過notify()和 notifyAll()的情況下醒來。防止假喚醒,保存信號的成員變量會檢查是否是自己的信號,如果不是的話,就繼續wait()。 public class MyWaitNotify3{MonitorObject myMonitorObject = new MonitorObject();boolean wasSignalled = false;public void doWait(){synchronized(myMonitorObject){while(!wasSignalled){try{myMonitorObject.wait();} catch(InterruptedException e){...}}//clear signal and continue running.wasSignalled = false;}}public void doNotify(){synchronized(myMonitorObject){wasSignalled = true;myMonitorObject.notify();}} } 6.多個線程等待相同信號 while 循環也可以解決當多線程在等待時,只需要喚醒一個線程,并且是使用nitifyAll()來喚醒的情況。 7.不要在字符串常量或全局對象中調用wait() 就是導致假喚醒的原因之一,并且可能會導致信號沒有接收到。?? 管程 (Monitor)是對多個工作線程實現互斥訪問共享資源的對象和模塊。管程實現了在一個時間點,最多只有一個線程在執行他的某個子程序。
  • 6 Java ThreadLocal
java中的ThreadLocal 可以讓變量只被同一個線程進行讀和寫操作。 創建: private ThreadLocal myThreadLocal = new ThreadLocal() 訪問: myThreadLocal.set(“local value”); String threadLocalValue = (String) myThreadLocal.get(); 如果不想用強制類型轉換,可以創建一個泛型化的ThreadLocal對象。 private ThreadLocal myThreadLocal1 = new ThreadLocal<String>();

?

轉載于:https://www.cnblogs.com/cheng2015/p/8487651.html

總結

以上是生活随笔為你收集整理的【转】JAVA 并发性和多线程 -- 读感 (二 线程间通讯,共享内存的机制)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。