电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

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

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

瀏覽:95日期: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
主站蜘蛛池模板: 电动葫芦|环链电动葫芦-北京凌鹰名优起重葫芦 | 水轮机密封网 | 水轮机密封产品研发生产厂家 | 成都顶呱呱信息技术有限公司-贷款_个人贷款_银行贷款在线申请 - 成都贷款公司 | 糖衣机,除尘式糖衣机,全自动糖衣机,泰州市长江制药机械有限公司 体感VRAR全息沉浸式3D投影多媒体展厅展会游戏互动-万展互动 | 北京西风东韵品牌与包装设计公司,创造视觉销售力! | 全自动五线打端沾锡机,全自动裁线剥皮双头沾锡机,全自动尼龙扎带机-东莞市海文能机械设备有限公司 | 耐酸碱胶管_耐腐蚀软管总成_化学品输送软管_漯河利通液压科技耐油耐磨喷砂软管|耐腐蚀化学软管 | 最新范文网_实用的精品范文美文网| Jaeaiot捷易科技-英伟达AI显卡模组/GPU整机服务器供应商 | 杭州标识标牌|文化墙|展厅|导视|户内外广告|发光字|灯箱|铭阳制作公司 - 杭州标识标牌|文化墙|展厅|导视|户内外广告|发光字|灯箱|铭阳制作公司 | NM-02立式吸污机_ZHCS-02软轴刷_二合一吸刷软轴刷-厦门地坤科技有限公司 | 双工位钻铣攻牙机-转换工作台钻攻中心-钻铣攻牙机一体机-浙江利硕自动化设备有限公司 | 净化车间_洁净厂房_净化公司_净化厂房_无尘室工程_洁净工程装修|改造|施工-深圳净化公司 | 油液红外光谱仪-油液监测系统-燃油嗅探仪-上海冉超光电科技有限公司 | 冷水机,风冷冷水机,水冷冷水机,螺杆冷水机专业制造商-上海祝松机械有限公司 | 北京网站建设-企业网站建设-建站公司-做网站-北京良言多米网络公司 | GAST/BRIWATEC/CINCINNATI/KARL-KLEIN/ZIEHL-ABEGG风机|亚喜科技 | 大功率金属激光焊接机价格_不锈钢汽车配件|光纤自动激光焊接机设备-东莞市正信激光科技有限公司 定制奶茶纸杯_定制豆浆杯_广东纸杯厂_[绿保佳]一家专业生产纸杯碗的厂家 | 期货软件-专业期货分析软件下载-云智赢 | 美能达分光测色仪_爱色丽分光测色仪-苏州方特电子科技有限公司 | 防水套管厂家_刚性防水套管_柔性防水套管_不锈钢防水套管-郑州中泰管道 | 防渗膜厂家|养殖防渗膜|水产养殖防渗膜-泰安佳路通工程材料有限公司 | 飞飞影视_热门电影在线观看_影视大全 | 校车_校车价格_19座幼儿园校车_幼儿园校车_大鼻子校车 | 杭州营业执照代办-公司变更价格-许可证办理流程_杭州福道财务管理咨询有限公司 | 安全,主动,被动,柔性,山体滑坡,sns,钢丝绳,边坡,防护网,护栏网,围栏,栏杆,栅栏,厂家 - 护栏网防护网生产厂家 | 丹佛斯压力传感器,WISE温度传感器,WISE压力开关,丹佛斯温度开关-上海力笙工业设备有限公司 | 高效复合碳源-多核碳源生产厂家-污水处理反硝化菌种一长隆科技库巴鲁 | 碳纤维布-植筋胶-灌缝胶-固特嘉加固材料公司| 氧化锆纤维_1800度高温退火炉_1800度高温烧结炉-南京理工宇龙新材料股份有限公司 | 安徽净化工程设计_无尘净化车间工程_合肥净化实验室_安徽创世环境科技有限公司 | 体坛网_体坛+_体坛周报新闻客户端| 减速机三参数组合探头|TSM803|壁挂式氧化锆分析仪探头-安徽鹏宸电气有限公司 | 量子管通环-自清洗过滤器-全自动反冲洗过滤器-北京罗伦过滤技术集团有限公司 | 领袖户外_深度旅游、摄影旅游、小团慢旅行、驴友网 | 二手回收公司_销毁处理公司_设备回收公司-找回收信息网 | 818手游网_提供当下热门APP手游_最新手机游戏下载 | 厚壁钢管-厚壁无缝钢管-小口径厚壁钢管-大口径厚壁钢管 - 聊城宽达钢管有限公司 | 福州仿石漆加盟_福建仿石漆厂家-外墙仿石漆加盟推荐铁壁金钢(福建)新材料科技有限公司有保障 | 众品地板网-地板品牌招商_地板装修设计_地板门户的首选网络媒体。 | 苏商学院官网 - 江苏地区唯一一家企业家自办的前瞻型、实操型商学院 |