亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術文章
文章詳情頁

Java線程內存模型,線程、工作內存、主內存

瀏覽:45日期:2022-09-05 14:32:09
java線程內存模型

線程、工作內存、主內存三者之間的交互關系圖:

Java線程內存模型,線程、工作內存、主內存

key edeas

所有線程共享主內存,每個線程有自己的工作內存

refreshing local memory to/from main memory must comply to JMM rules

產生線程安全的原因

線程的working memory是cpu的寄存器和高速緩存的抽象描述:現在的計算機,cpu在計算的時候,并不總是從內存讀取數據,它的數據讀取順序優先級 是: 寄存器-高速緩存-內存 。線程耗費的是CPU,線程計算的時候,原始的數據來自內存,在計算過程中,有些數據可能被頻繁讀取,這些數據被存儲在寄存器和高速緩存中,當線程計算完后,這些緩存的數據在適當的時候應該寫回內存。當多個線程同時讀寫某個內存數據時,就會產生多線程并發問題,涉及到三個特 性:原子性,有序性,可見性。 支持多線程的平臺都會面臨 這種問題,運行在多線程平臺上支持多線程的語言應該提供解決該問題的方案。

JVM是一個虛擬的計算機,它也會面臨多線程并發問題,java程序運行在java虛擬機平臺上,java程序員不可能直接去控制底層線程對寄存器高速緩存內存之間的同步,那么java從語法層面,應該給開發人員提供一種解決方案,這個方案就是諸如 synchronized, volatile,鎖機制(如同步塊,就緒隊 列,阻塞隊列)等等。這些方案只是語法層面的,但我們要從本質上去理解它;

每個線程都有自己的執行空間(即工作內存),線程執行的時候用到某變量,首先要將變量從主內存拷貝的自己的工作內存空間,然后對變量進行操作:讀取,修改,賦值等,這些均在工作內存完成,操作完成后再將變量寫回主內存;

各個線程都從主內存中獲取數據,線程之間數據是不可見的;打個比方:主內存變量A原始值為1,線程1從主內存取出變量A,修改A的值為2,在線程1未將變量A寫回主內存的時候,線程2拿到變量A的值仍然為1;

這便引出“可見性”的概念:當一個共享變量在多個線程的工作內存中都有副本時,如果一個線程修改了這個共享變量的副本值,那么其他線程應該能夠看到這個被修改后的值,這就是多線程的可見性問題。

普通變量情況:如線程A修改了一個普通變量的值,然后向主內存進行寫回,另外一條線程B在線程A回寫完成了之后再從主內存進行讀取操作,新變量的值才會對線程B可見;

如何保證線程安全

編寫線程安全的代碼,本質上就是管理對狀態(state)的訪問,而且通常都是共享的、可變的狀態。這里的狀態就是對象的變量(靜態變量和實例變量)

線程安全的前提是該變量是否被多個線程訪問, 保證對象的線程安全性需要使用同步來協調對其可變狀態的訪問;若是做不到這一點,就會導致臟數據和其他不可預期的后果。無論何時,只要有多于一個的線程訪問給定的狀態變量,而且其中某個線程會寫入該變量,此時必須使用同步來協調線程對該變量的訪問。Java中首要的同步機制是synchronized關鍵字,它提供了獨占鎖。除此之外,術語“同步”還包括volatile變量,顯示鎖和原子變量的使用。

在沒有正確同步的情況下,如果多個線程訪問了同一個變量,你的程序就存在隱患。有3種方法修復它:

l 不要跨線程共享變量;

l 使狀態變量為不可變的;或者

l 在任何訪問狀態變量的時候使用同步。

volatile要求程序對變量的每次修改,都寫回主內存,這樣便對其它線程課件,解決了可見性的問題,但是不能保證數據的一致性;特別注意:原子操作:根據Java規范,對于基本類型的賦值或者返回值操作,是原子操作。但這里的基本數據類型不包括long和double, 因為JVM看到的基本存儲單位是32位,而long 和double都要用64位來表示。所以無法在一個時鐘周期內完成

通俗的講一個對象的狀態就是它的數據,存儲在狀態變量中,比如實例域或者靜態域;無論何時,只要多于一個的線程訪問給定的狀態變量。而且其中某個線程會寫入該變量,此時必須使用同步來協調線程對該變量的訪問;

同步鎖:每個JAVA對象都有且只有一個同步鎖,在任何時刻,最多只允許一個線程擁有這把鎖。

當一個線程試圖訪問帶有synchronized(this)標記的代碼塊時,必須獲得 this關鍵字引用的對象的鎖,在以下的兩種情況下,本線程有著不同的命運。

1、 假如這個鎖已經被其它的線程占用,JVM就會把這個線程放到本對象的鎖池中。本線程進入阻塞狀態。鎖池中可能有很多的線程,等到其他的線程釋放了鎖,JVM就會從鎖池中隨機取出一個線程,使這個線程擁有鎖,并且轉到就緒狀態。

2、 假如這個鎖沒有被其他線程占用,本線程會獲得這把鎖,開始執行同步代碼塊。

(一般情況下在執行同步代碼塊時不會釋放同步鎖,但也有特殊情況會釋放對象鎖

如在執行同步代碼塊時,遇到異常而導致線程終止,鎖會被釋放;在執行代碼塊時,執行了鎖所屬對象的wait()方法,這個線程會釋放對象鎖,進入對象的等待池中)

Synchronized關鍵字保證了數據讀寫一致和可見性等問題,但是他是一種阻塞的線程控制方法,在關鍵字使用期間,所有其他線程不能使用此變量,這就引出了一種叫做非阻塞同步的控制線程安全的需求;

ThreadLocal 解析

顧名思義它是local variable(線程局部變量)。它的功用非常簡單,就是為每一個使用該變量的線程都提供一個變量值的副本,是每一個線程都可以獨立地改變自己的副本,而不會和其它線程的副本沖突。從線程的角度看,就好像每一個線程都完全擁有該變量。

每個線程都保持對其線程局部變量副本的隱式引用,只要線程是活動的并且 ThreadLocal 實例是可訪問的;在線程消失之后,其線程局部實例的所有副本都會被垃圾回收(除非存在對這些副本的其他引用)。

Object wait()和notify()方法解析

Object的wait()和notify()、notifyAll()方法,使用一個對象作為鎖,然后調用wait()就會掛起當前線程,同時釋放對象鎖;

notify()使用要首先獲取對象鎖,然后才能喚醒被掛起的線程(因為等待對象鎖而掛起的)

notifyAll():喚醒在此對象監視器上等待的所有線程。

wait()在其他線程調用此對象的 notify() 方法或 notifyAll() 方法前,導致當前線程等待。

拋出: IllegalMonitorStateException - 如果當前線程不是此對象監視器的所有者

package com.taobao.concurrency;public class WaitTest { public static String a = '';// 作為監視器對象 public static void main(String[] args) throws InterruptedException {WaitTest wa = new WaitTest();TestTask task = wa.new TestTask();Thread t = new Thread(task);t.start();Thread.sleep(12000);for (int i = 5; i > 0; i--) { System.out.println('快喚醒掛起的線程************'); Thread.sleep(1000);}System.out.println('收到,馬上!喚醒掛起的線程************');synchronized (a) { a.notifyAll();} } class TestTask implements Runnable {@Overridepublic void run() { synchronized (a) {try { for (int i = 10; i > 0; i--) {Thread.sleep(1000);System.out.println('我在運行 ***************'); } a.wait(); for (int i = 10; i > 0; i--) {System.out.println('謝謝喚醒**********又開始運行了*******'); }} catch (InterruptedException e) { e.printStackTrace();} }} }}

用wait notify 解決生產者消費者問題代碼:

package com.taobao.concurrency;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;class Meal { private final int orderNum; public Meal(int orderNum) {this.orderNum = orderNum; } public String toString() {return 'Meal ' + orderNum; }}class WaitPerson implements Runnable { private Restaurant restaurant; public WaitPerson(Restaurant r) {this.restaurant = r; } @Override public void run() {try { while (!Thread.interrupted()) {synchronized (this) { while (restaurant.meal == null)wait();// ..for the chef to produce a meal}System.out.println('WaitPerson got' + restaurant.meal);synchronized (restaurant.chef) { restaurant.meal = null; restaurant.chef.notifyAll();// ready for another} } TimeUnit.MICROSECONDS.sleep(100);} catch (InterruptedException e) { System.out.println('WaitPerson interrupted');} }}class Chef implements Runnable { private Restaurant restaurant; private int count = 0; public Chef(Restaurant r) {this.restaurant = r; } @Override public void run() {try { while (!Thread.interrupted()) {synchronized (this) { while (restaurant.meal != null)wait();// ...for the meal to be taken}if (++count == 10) { System.out.println('Out of food,closing'); restaurant.exec.shutdownNow();}System.out.println('Order up!');synchronized (restaurant.waitPerson) { restaurant.meal = new Meal(count); restaurant.waitPerson.notifyAll();} }} catch (InterruptedException e) {} }}public class Restaurant { Meal meal; ExecutorService exec = Executors.newCachedThreadPool(); WaitPerson waitPerson = new WaitPerson(this); Chef chef = new Chef(this); public Restaurant() {exec.execute(chef);exec.execute(waitPerson); } public static void main(String[] args) {new Restaurant(); }}

用ArrayBlockingQueue解決生產者消費者問題 ;默認使用的是非公平鎖

take():取走BlockingQueue里排在首位的對象,若BlockingQueue為空,阻斷進入等待狀態直到Blocking有新的對象被加入為止,若請求不到此線程被加入阻塞隊列;

如果使用公平鎖,當有內容可以消費時,會從隊首取出消費者線程進行消費(即等待時間最長的線程)

add(anObject):把anObject加到BlockingQueue里,即如果BlockingQueue可以容納,則返回true,否則招聘異常

package com.taobao.concurrency;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;public class TestBlockingQueues { public static void main(String[] args) {BlockingQueue<String> queue = new ArrayBlockingQueue<String>(20);Thread pro = new Thread(new Producer(queue), '生產者');pro.start();for (int i = 0; i < 10; i++) { Thread t = new Thread(new Concumer(queue), '消費者 ' + i); t.start();} }}class Producer implements Runnable { BlockingQueue<String> queue; public Producer(BlockingQueue<String> queue) {this.queue = queue; } @Override public void run() {int i = 0;while (true) { try {System.out.println('生產者生產食物, 食物編號為:' + i);queue.put(' 食物 ' + i++);Thread.sleep(1000); } catch (InterruptedException e) {System.out.println('生產者被中斷'); }} }}class Concumer implements Runnable { BlockingQueue<String> queue; public Concumer(BlockingQueue<String> queue) {this.queue = queue; } @Override public void run() {while (true) { try {System.out.println(Thread.currentThread().getName() + '消費:'+ queue.take()); } catch (InterruptedException e) {System.out.println('消費者被中斷'); }} }}執行結果:消費者 0 請求消費消費者 2 請求消費消費者 4 請求消費消費者 6 請求消費消費者 8 請求消費消費者 5 請求消費生產者生產食物, 食物編號為:0消費者 0消費: 食物 0消費者 1 請求消費消費者 3 請求消費消費者 7 請求消費消費者 9 請求消費消費者 0 請求消費生產者生產食物, 食物編號為:1消費者 2消費: 食物 1消費者 2 請求消費生產者生產食物, 食物編號為:2消費者 4消費: 食物 2消費者 4 請求消費生產者生產食物, 食物編號為:3消費者 6消費: 食物 3消費者 6 請求消費生產者生產食物, 食物編號為:4消費者 8消費: 食物 4消費者 8 請求消費生產者生產食物, 食物編號為:5消費者 5消費: 食物 5消費者 5 請求消費生產者生產食物, 食物編號為:6消費者 1消費: 食物 6消費者 1 請求消費生產者生產食物, 食物編號為:7消費者 3消費: 食物 7消費者 3 請求消費生產者生產食物, 食物編號為:8消費者 7消費: 食物 8消費者 7 請求消費生產者生產食物, 食物編號為:9消費者 9消費: 食物 9消費者 9 請求消費

多個生產者,多個消費者

package com.taobao.concurrency;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;public class TestBlockingQueues { public static void main(String[] args) {BlockingQueue<String> queue = new ArrayBlockingQueue<String>(20);for (int i = 0; i < 10; i++) { Thread pro = new Thread(new Producer(queue), '生產者' + i); pro.start();}for (int i = 0; i < 10; i++) { Thread t = new Thread(new Concumer(queue), '消費者 ' + i); t.start();} }}class Producer implements Runnable { BlockingQueue<String> queue; public Producer(BlockingQueue<String> queue) {this.queue = queue; } @Override public void run() {int i = 0;while (true) { try { System.out.println(Thread.currentThread().getName() + '生產食物, 食物編號為:' + Thread.currentThread().getName() + i); queue.put(' 食物 ' + Thread.currentThread().getName() + i++); Thread.sleep(10000); } catch (InterruptedException e) {System.out.println('生產者被中斷'); }} }}class Concumer implements Runnable { BlockingQueue<String> queue; public Concumer(BlockingQueue<String> queue) {this.queue = queue; } @Override public void run() {while (true) { System.out.println(Thread.currentThread().getName() + ' 請求消費'); try {System.out.println(Thread.currentThread().getName() + '消費:'+ queue.take());Thread.sleep(100); } catch (InterruptedException e) {System.out.println('消費者被中斷'); }} }}生產者0生產食物, 食物編號為:生產者00生產者2生產食物, 食物編號為:生產者20生產者1生產食物, 食物編號為:生產者10生產者3生產食物, 食物編號為:生產者30生產者4生產食物, 食物編號為:生產者40生產者6生產食物, 食物編號為:生產者60生產者8生產食物, 食物編號為:生產者80生產者5生產食物, 食物編號為:生產者50生產者7生產食物, 食物編號為:生產者70生產者9生產食物, 食物編號為:生產者90消費者 0 請求消費消費者 0消費: 食物 生產者00消費者 2 請求消費消費者 2消費: 食物 生產者20消費者 1 請求消費消費者 1消費: 食物 生產者10消費者 4 請求消費消費者 4消費: 食物 生產者30消費者 3 請求消費消費者 6 請求消費消費者 6消費: 食物 生產者40消費者 3消費: 食物 生產者60消費者 8 請求消費消費者 8消費: 食物 生產者80消費者 5 請求消費消費者 5消費: 食物 生產者50消費者 7 請求消費消費者 7消費: 食物 生產者70消費者 9 請求消費消費者 9消費: 食物 生產者90消費者 0 請求消費消費者 1 請求消費消費者 2 請求消費消費者 4 請求消費消費者 3 請求消費消費者 5 請求消費消費者 7 請求消費消費者 9 請求消費消費者 6 請求消費消費者 8 請求消費生產者0生產食物, 食物編號為:生產者01消費者 0消費: 食物 生產者01生產者2生產食物, 食物編號為:生產者21生產者4生產食物, 食物編號為:生產者41消費者 1消費: 食物 生產者21生產者1生產食物, 食物編號為:生產者11消費者 2消費: 食物 生產者41消費者 4消費: 食物 生產者11生產者3生產食物, 食物編號為:生產者31

條件隊列解釋:

Condition queuesare like the 'toast is ready' bell on your toaster. If you are listening for it, you are notified promptly when your toast is ready and can drop what you are doing (or not, maybe you want to finish the newspaper first) and get your toast. If you are not listening for it (perhaps you went outside to get the newspaper), you could miss the notification, but on return to the kitchen you can observe the state of the toaster and either retrieve the toast if it is finished or start listening for the bell again if it is not.

基于條件的:多線程情況下,某個條件在某個時刻為假,不代表一直為假,可能到某個時刻就好了!

Lock 使用的默認 為非公平鎖;condition對象繼承了與之相關的鎖的共平性特性,如果是公平的鎖,線程會依照FIFO的順序從Condition.wait中被釋放;ArrayBlockingQueue中有一個比較不好的地方,生產者每次生產完之后,都要通知消費者,至于有沒有性能損失TODO

來自:https://zhuanlan.zhihu.com/p/25474331

標簽: Java
主站蜘蛛池模板: 亚洲欧美日韩一区二区在线观看 | 久久精品这里有 | 欧美高清性刺激毛片 | 久久99国产精品久久99果冻传媒 | 美女国产网站 | 国产精品永久免费视频观看 | 国产免费一级高清淫曰本片 | 香蕉精品在线 | 国产成人高清精品免费观看 | a级片免费在线观看 | 欧美日韩网 | 自怕偷自怕亚洲精品 | 国产精品无码永久免费888 | 高潮岳喷我一脸 | 久久国产精品-久久精品 | 国产久热香蕉在线观看 | 亚洲精品国产第1页 | 国产美女久久久久 | 亚洲高清国产一区二区三区 | 94在线成人免费视频 | 国产色婷婷亚洲99精品小说 | 大学生久久香蕉国产线观看 | 亚洲精品视频在线免费 | 99视频精品全部国产盗摄视频 | 中文字幕在线视频不卡 | 亚洲高清在线观看视频 | 中文字幕精品一区二区日本大胸 | 亚洲欲色| 26uuu在线 | 91麻豆视频| 亚洲色图欧洲色图 | 91播放在线| 免费一级淫片aa | 欧美大片天天免费看视频 | 国产久视频观看 | 91精品国产露脸在线 | 日本一区二区三区四区无限 | 欧美视频在线一区二区三区 | 亚洲综合第一区 | 免费观看好看的国产片 | 播放一级黄色录像 |