奶头挺立呻吟高潮av全片,成人试看120秒体验区,性欧美极品v,A片高潮抽搐揉捏奶头视频

java語(yǔ)言

Java的內(nèi)存模型

時(shí)間:2024-09-22 00:02:19 java語(yǔ)言 我要投稿
  • 相關(guān)推薦

Java的內(nèi)存模型

  在Java語(yǔ)言中,采用的是共享內(nèi)存模型來(lái)實(shí)現(xiàn)多線程之間的信息交換和數(shù)據(jù)同步的。線程之間通過(guò)共享程序公共的狀態(tài),通過(guò)讀-寫內(nèi)存中公共狀態(tài)的方式來(lái)進(jìn)行隱式的通信。同步指的是程序在控制多個(gè)線程之間執(zhí)行程序的相對(duì)順序的機(jī)制,在共享內(nèi)存模型中,同步是顯式的,程序員必須顯式指定某個(gè)方法/代碼塊需要在多線程之間互斥執(zhí)行。下面是小編為大家?guī)?lái)的Java的內(nèi)存模型,歡迎閱讀。

  概述

  Java內(nèi)存模型的主要目標(biāo)是定義程序中各個(gè)變量的訪問(wèn)規(guī)則,即在JVM中將變量存儲(chǔ)到內(nèi)存和從內(nèi)存中取出變量這樣的底層細(xì)節(jié)。此處的變量與Java編程里面的變量有所不同,它包含了實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素,但不包含局部變量和方法參數(shù),因?yàn)楹笳呤蔷程私有的,不會(huì)共享,當(dāng)然不存在數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題(如果局部變量是一個(gè)reference引用類型,它引用的對(duì)象在Java堆中可被各個(gè)線程共享,但是reference引用本身在Java棧的局部變量表中,是線程私有的)。為了獲得較高的執(zhí)行效能,Java內(nèi)存模型并沒(méi)有限制執(zhí)行引起使用處理器的特定寄存器或者緩存來(lái)和主內(nèi)存進(jìn)行交互,也沒(méi)有限制即時(shí)編譯器進(jìn)行調(diào)整代碼執(zhí)行順序這類優(yōu)化措施。

  JMM規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存(Main Memory)中。每個(gè)線程還有自己的工作內(nèi)存(Working Memory),線程的工作內(nèi)存中保存了該線程使用到的變量的主內(nèi)存的副本拷貝,線程對(duì)變量的所有操作(讀取、賦值等)都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存中的變量(volatile變量仍然有工作內(nèi)存的拷貝,但是由于它特殊的操作順序性規(guī)定,所以看起來(lái)如同直接在主內(nèi)存中讀寫訪問(wèn)一般)。不同的線程之間也無(wú)法直接訪問(wèn)對(duì)方工作內(nèi)存中的變量,線程之間值的傳遞都需要通過(guò)主內(nèi)存來(lái)完成。

  線程1和線程2要想進(jìn)行數(shù)據(jù)的交換一般要經(jīng)歷下面的步驟:

  1.線程1把工作內(nèi)存1中的更新過(guò)的共享變量刷新到主內(nèi)存中去。

  2.線程2到主內(nèi)存中去讀取線程1刷新過(guò)的共享變量,然后copy一份到工作內(nèi)存2中去。

  內(nèi)存模型的特性

  Java內(nèi)存模型是圍繞著并發(fā)編程中原子性、可見(jiàn)性、有序性這三個(gè)特征來(lái)建立的,那我們依次看一下這三個(gè)特征:

  原子性(Atomicity)

  原子性是指一個(gè)操作不能被打斷,要么全部執(zhí)行完畢,要么不執(zhí)行。在這點(diǎn)上有點(diǎn)類似于事務(wù)操作,要么全部執(zhí)行成功,要么回退到執(zhí)行該操作之前的狀態(tài)。

  基本類型數(shù)據(jù)的訪問(wèn)大都是原子操作,long 和double類型的變量是64位,但是在32位JVM中,32位的JVM會(huì)將64位數(shù)據(jù)的讀寫操作分為2次32位的讀寫操作來(lái)進(jìn)行,這就導(dǎo)致了long、double類型的變量在32位虛擬機(jī)中是非原子操作,數(shù)據(jù)有可能會(huì)被破壞,也就意味著多個(gè)線程在并發(fā)訪問(wèn)的時(shí)候是線程非安全的。

  下面我們來(lái)演示這個(gè)32位JVM下,對(duì)64位long類型的數(shù)據(jù)的訪問(wèn)的問(wèn)題:

  【代碼1】

  public class NotAtomicity {

  //靜態(tài)變量t

  public static long t = 0;

  //靜態(tài)變量t的get方法

  public static long getT() {

  return t;

  }

  //靜態(tài)變量t的set方法

  public static void setT(long t) {

  NotAtomicity.t = t;

  }

  //改變變量t的線程

  public static class ChangeT implements Runnable{

  private long to;

  public ChangeT(long to) {

  this.to = to;

  }

  public void run() {

  //不斷的將long變量設(shè)值到 t中

  while (true) {

  NotAtomicity.setT(to);

  //將當(dāng)前線程的執(zhí)行時(shí)間片段讓出去,以便由線程調(diào)度機(jī)制重新決定哪個(gè)線程可以執(zhí)行

  Thread.yield();

  }

  }

  }

  //讀取變量t的線程,若讀取的值和設(shè)置的值不一致,說(shuō)明變量t的數(shù)據(jù)被破壞了,即線程不安全

  public static class ReadT implements Runnable{

  public void run() {

  //不斷的讀取NotAtomicity的t的值

  while (true) {

  long tmp = NotAtomicity.getT();

  //比較是否是自己設(shè)值的其中一個(gè)

  if (tmp != 100L && tmp != 200L && tmp != -300L && tmp != -400L) {

  //程序若執(zhí)行到這里,說(shuō)明long類型變量t,其數(shù)據(jù)已經(jīng)被破壞了

  System.out.println(tmp);

  }

  ////將當(dāng)前線程的執(zhí)行時(shí)間片段讓出去,以便由線程調(diào)度機(jī)制重新決定哪個(gè)線程可以執(zhí)行

  Thread.yield();

  }

  }

  }

  public static void main(String[] args) {

  new Thread(new ChangeT(100L)).start();

  new Thread(new ChangeT(200L)).start();

  new Thread(new ChangeT(-300L)).start();

  new Thread(new ChangeT(-400L)).start();

  new Thread(new ReadT()).start();

  }

  }

  我們創(chuàng)建了4個(gè)線程來(lái)對(duì)long類型的變量t進(jìn)行賦值,賦值分別為100,200,-300,-400,有一個(gè)線程負(fù)責(zé)讀取變量t,如果正常的話,讀取到的t的值應(yīng)該是我們賦值中的一個(gè),但是在32的JVM中(ps: 64位的就別想了),事情會(huì)出乎預(yù)料。如果程序正常的話,我們控制臺(tái)不會(huì)有任何的輸出,可實(shí)際上,程序一運(yùn)行,控制臺(tái)就輸出了下面的信息:

  -4294967096

  4294966896

  -4294967096

  -4294967096

  4294966896

  之所以會(huì)出現(xiàn)上面的情況,是因?yàn)樵?2位JVM中,64位的long數(shù)據(jù)的讀和寫都不是原子操作,即不具有原子性,并發(fā)的時(shí)候相互干擾了。

  32位的JVM中,要想保證對(duì)long、double類型數(shù)據(jù)的操作的原子性,可以對(duì)訪問(wèn)該數(shù)據(jù)的方法進(jìn)行同步,就像下面的:

  【代碼2】

  public class Atomicity {

  //靜態(tài)變量t

  public static long t = 0;

  //靜態(tài)變量t的get方法,同步方法

  public synchronized static long getT() {

  return t;

  }

  //靜態(tài)變量t的set方法,同步方法

  public synchronized static void setT(long t) {

  Atomicity.t = t;

  }

  //改變變量t的線程

  public static class ChangeT implements Runnable{

  private long to;

  public ChangeT(long to) {

  this.to = to;

  }

  public void run() {

  //不斷的將long變量設(shè)值到 t中

  while (true) {

  Atomicity.setT(to);

  //將當(dāng)前線程的執(zhí)行時(shí)間片段讓出去,以便由線程調(diào)度機(jī)制重新決定哪個(gè)線程可以執(zhí)行

  Thread.yield();

  }

  }

  }

  //讀取變量t的線程,若讀取的值和設(shè)置的值不一致,說(shuō)明變量t的數(shù)據(jù)被破壞了,即線程不安全

  public static class ReadT implements Runnable{

  public void run() {

  //不斷的讀取NotAtomicity的t的值

  while (true) {

  long tmp = Atomicity.getT();

  //比較是否是自己設(shè)值的其中一個(gè)

  if (tmp != 100L && tmp != 200L && tmp != -300L && tmp != -400L) {

  //程序若執(zhí)行到這里,說(shuō)明long類型變量t,其數(shù)據(jù)已經(jīng)被破壞了

  System.out.println(tmp);

  }

  ////將當(dāng)前線程的執(zhí)行時(shí)間片段讓出去,以便由線程調(diào)度機(jī)制重新決定哪個(gè)線程可以執(zhí)行

  Thread.yield();

  }

  }

  }

  public static void main(String[] args) {

  new Thread(new ChangeT(100L)).start();

  new Thread(new ChangeT(200L)).start();

  new Thread(new ChangeT(-300L)).start();

  new Thread(new ChangeT(-400L)).start();

  new Thread(new ReadT()).start();

  }

  }

  這樣做的話,可以保證對(duì)64位數(shù)據(jù)操作的原子性。

  可見(jiàn)性

  一個(gè)線程對(duì)共享變量做了修改之后,其他的線程立即能夠看到(感知到)該變量這種修改(變化)。

  Java內(nèi)存模型是通過(guò)將在工作內(nèi)存中的變量修改后的值同步到主內(nèi)存,在讀取變量前從主內(nèi)存刷新最新值到工作內(nèi)存中,這種依賴主內(nèi)存的方式來(lái)實(shí)現(xiàn)可見(jiàn)性的。

  無(wú)論是普通變量還是volatile變量都是如此,區(qū)別在于:volatile的特殊規(guī)則保證了volatile變量值修改后的新值立刻同步到主內(nèi)存,每次使用volatile變量前立即從主內(nèi)存中刷新,因此volatile保證了多線程之間的操作變量的可見(jiàn)性,而普通變量則不能保證這一點(diǎn)。

  除了volatile關(guān)鍵字能實(shí)現(xiàn)可見(jiàn)性之外,還有synchronized,Lock,final也是可以的。

  使用synchronized關(guān)鍵字,在同步方法/同步塊開始時(shí)(Monitor Enter),使用共享變量時(shí)會(huì)從主內(nèi)存中刷新變量值到工作內(nèi)存中(即從主內(nèi)存中讀取最新值到線程私有的工作內(nèi)存中),在同步方法/同步塊結(jié)束時(shí)(Monitor Exit),會(huì)將工作內(nèi)存中的變量值同步到主內(nèi)存中去(即將線程私有的工作內(nèi)存中的值寫入到主內(nèi)存進(jìn)行同步)。

  使用Lock接口的最常用的實(shí)現(xiàn)ReentrantLock(重入鎖)來(lái)實(shí)現(xiàn)可見(jiàn)性:當(dāng)我們?cè)诜椒ǖ拈_始位置執(zhí)行l(wèi)ock.lock()方法,這和synchronized開始位置(Monitor Enter)有相同的語(yǔ)義,即使用共享變量時(shí)會(huì)從主內(nèi)存中刷新變量值到工作內(nèi)存中(即從主內(nèi)存中讀取最新值到線程私有的工作內(nèi)存中),在方法的最后finally塊里執(zhí)行l(wèi)ock.unlock()方法,和synchronized結(jié)束位置(Monitor Exit)有相同的語(yǔ)義,即會(huì)將工作內(nèi)存中的變量值同步到主內(nèi)存中去(即將線程私有的工作內(nèi)存中的值寫入到主內(nèi)存進(jìn)行同步)。

  final關(guān)鍵字的可見(jiàn)性是指:被final修飾的變量,在構(gòu)造函數(shù)數(shù)一旦初始化完成,并且在構(gòu)造函數(shù)中并沒(méi)有把“this”的引用傳遞出去(“this”引用逃逸是很危險(xiǎn)的,其他的線程很可能通過(guò)該引用訪問(wèn)到只“初始化一半”的對(duì)象),那么其他線程就可以看到final變量的值。

  有序性

  對(duì)于一個(gè)線程的代碼而言,我們總是以為代碼的執(zhí)行是從前往后的,依次執(zhí)行的。這么說(shuō)不能說(shuō)完全不對(duì),在單線程程序里,確實(shí)會(huì)這樣執(zhí)行;但是在多線程并發(fā)時(shí),程序的執(zhí)行就有可能出現(xiàn)亂序。用一句話可以總結(jié)為:在本線程內(nèi)觀察,操作都是有序的;如果在一個(gè)線程中觀察另外一個(gè)線程,所有的操作都是無(wú)序的。前半句是指“線程內(nèi)表現(xiàn)為串行語(yǔ)義(WithIn Thread As-if-Serial Semantics)”,后半句是指“指令重排”現(xiàn)象和“工作內(nèi)存和主內(nèi)存同步延遲”現(xiàn)象。

  Java提供了兩個(gè)關(guān)鍵字volatile和synchronized來(lái)保證多線程之間操作的有序性,volatile關(guān)鍵字本身通過(guò)加入內(nèi)存屏障來(lái)禁止指令的重排序,而synchronized關(guān)鍵字通過(guò)一個(gè)變量在同一時(shí)間只允許有一個(gè)線程對(duì)其進(jìn)行加鎖的規(guī)則來(lái)實(shí)現(xiàn),在單線程程序中,不會(huì)發(fā)生“指令重排”和“工作內(nèi)存和主內(nèi)存同步延遲”現(xiàn)象,只在多線程程序中出現(xiàn)。

  happens-before原則

  Java內(nèi)存模型中定義的兩項(xiàng)操作之間的次序關(guān)系,如果說(shuō)操作A先行發(fā)生于操作B,操作A產(chǎn)生的影響能作B觀察到,“影響”包含了修改了內(nèi)存享變量的值、發(fā)送了消息、調(diào)用了方法等。

  下面是Java內(nèi)存模型下一些”天然的“happens-before關(guān)系,這些happens-before關(guān)系無(wú)須任何同步器協(xié)助就已經(jīng)存在,可以在編碼中直接使用。如果兩個(gè)操作之間的關(guān)系不在此列,并且無(wú)法從下列規(guī)則推導(dǎo)出來(lái)的話,它們就沒(méi)有順序性保障,虛擬機(jī)可以對(duì)它們進(jìn)行隨意地重排序。

  程序次序規(guī)則(Pragram Order Rule):在一個(gè)線程內(nèi),按照程序代碼順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作。準(zhǔn)確地說(shuō)應(yīng)該是控制流順序而不是程序代碼順序,因?yàn)橐紤]分支、循環(huán)結(jié)構(gòu)。

  管程鎖定規(guī)則(Monitor Lock Rule):一個(gè)unlock操作先行發(fā)生于后面對(duì)同一個(gè)鎖的lock操作。這里必須強(qiáng)調(diào)的是同一個(gè)鎖,而”后面“是指時(shí)間上的先后順序。

  volatile變量規(guī)則(Volatile Variable Rule):對(duì)一個(gè)volatile變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀取操作,這里的”后面“同樣指時(shí)間上的先后順序。

  線程啟動(dòng)規(guī)則(Thread Start Rule):Thread對(duì)象的start()方法先行發(fā)生于此線程的每一個(gè)動(dòng)作。

  線程終于規(guī)則(Thread Termination Rule):線程中的所有操作都先行發(fā)生于對(duì)此線程的終止檢測(cè),我們可以通過(guò)Thread.join()方法結(jié)束,Thread.isAlive()的返回值等作段檢測(cè)到線程已經(jīng)終止執(zhí)行。

  線程中斷規(guī)則(Thread Interruption Rule):對(duì)線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生,可以通過(guò)Thread.interrupted()方法檢測(cè)是否有中斷發(fā)生。

  對(duì)象終結(jié)規(guī)則(Finalizer Rule):一個(gè)對(duì)象初始化完成(構(gòu)造方法執(zhí)行完成)先行發(fā)生于它的finalize()方法的開始。

  傳遞性(Transitivity):如果操作A先行發(fā)生于操作B,操作B先行發(fā)生于操作C,那就可以得出操作A先行發(fā)生于操作C的結(jié)論。

  一個(gè)操作”時(shí)間上的先發(fā)生“不代表這個(gè)操作會(huì)是“先行發(fā)生",那如果一個(gè)操作"先行發(fā)生"是否就能推導(dǎo)出這個(gè)操作必定是"時(shí)間上的先發(fā)生"呢?也是不成立的,一個(gè)典型的例子就是指令重排序。所以時(shí)間上的先后順序與happens-before原則之間基本沒(méi)有什么關(guān)系,所以衡量并發(fā)安全問(wèn)題一切必須以happens-before 原則為準(zhǔn)。


【Java的內(nèi)存模型】相關(guān)文章:

Java內(nèi)存回收11-25

Java編程節(jié)省內(nèi)存的方法12-16

JAVA對(duì)象創(chuàng)造及內(nèi)存布局介紹12-04

如何解決java內(nèi)存泄漏的問(wèn)題03-03

查看java對(duì)象所占的內(nèi)存大小的方法12-04

Java編程節(jié)省內(nèi)存的方法有哪些11-21

Windows內(nèi)存診斷07-07

2g內(nèi)存的臺(tái)式機(jī)怎么添加內(nèi)存12-04

電腦內(nèi)存怎么清理11-30

主站蜘蛛池模板: 类乌齐县| 珲春市| 县级市| 吉水县| 通榆县| 鱼台县| 舟曲县| 横峰县| 苗栗市| 浙江省| 青川县| 白银市| 平湖市| 阜宁县| 光山县| 个旧市| 拜城县| 固安县| 松原市| 淄博市| 监利县| 汝城县| 新津县| 仲巴县| 定结县| 泾源县| 云林县| 梁平县| 隆昌县| 比如县| 上饶市| 井研县| 雅安市| 修武县| 宜丰县| 秦皇岛市| 平谷区| 禄劝| 措勤县| 中牟县| 肥西县|