日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

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

詳解java安全編碼指南之可見性和原子性

瀏覽:159日期:2022-08-11 11:50:06
目錄不可變對象的可見性保證共享變量的復合操作的原子性保證多個Atomic原子類操作的原子性保證方法調用鏈的原子性讀寫64bits的值不可變對象的可見性

不可變對象就是初始化之后不能夠被修改的對象,那么是不是類中引入了不可變對象,所有對不可變對象的修改都立馬對所有線程可見呢?

實際上,不可變對象只能保證在多線程環境中,對象使用的安全性,并不能夠保證對象的可見性。

先來討論一下可變性,我們考慮下面的一個例子:

public final class ImmutableObject { private final int age; public ImmutableObject(int age){this.age=age; }}

我們定義了一個ImmutableObject對象,class是final的,并且里面的唯一字段也是final的。所以這個ImmutableObject初始化之后就不能夠改變。

然后我們定義一個類來get和set這個ImmutableObject:

public class ObjectWithNothing { private ImmutableObject refObject; public ImmutableObject getImmutableObject(){return refObject; } public void setImmutableObject(int age){this.refObject=new ImmutableObject(age); }}

上面的例子中,我們定義了一個對不可變對象的引用refObject,然后定義了get和set方法。

注意,雖然ImmutableObject這個類本身是不可變的,但是我們對該對象的引用refObject是可變的。這就意味著我們可以調用多次setImmutableObject方法。

再來討論一下可見性。

上面的例子中,在多線程環境中,是不是每次setImmutableObject都會導致getImmutableObject返回一個新的值呢?

答案是否定的。

當把源碼編譯之后,在編譯器中生成的指令的順序跟源碼的順序并不是完全一致的。處理器可能采用亂序或者并行的方式來執行指令(在JVM中只要程序的最終執行結果和在嚴格串行環境中執行結果一致,這種重排序是允許的)。并且處理器還有本地緩存,當將結果存儲在本地緩存中,其他線程是無法看到結果的。除此之外緩存提交到主內存的順序也肯能會變化。

怎么解決呢?

最簡單的解決可見性的辦法就是加上volatile關鍵字,volatile關鍵字可以使用java內存模型的happens-before規則,從而保證volatile的變量修改對所有線程可見。

public class ObjectWithVolatile { private volatile ImmutableObject refObject; public ImmutableObject getImmutableObject(){return refObject; } public void setImmutableObject(int age){this.refObject=new ImmutableObject(age); }}

另外,使用鎖機制,也可以達到同樣的效果:

public class ObjectWithSync { private ImmutableObject refObject; public synchronized ImmutableObject getImmutableObject(){return refObject; } public synchronized void setImmutableObject(int age){this.refObject=new ImmutableObject(age); }}

最后,我們還可以使用原子類來達到同樣的效果:

public class ObjectWithAtomic { private final AtomicReference<ImmutableObject> refObject= new AtomicReference<>(); public ImmutableObject getImmutableObject(){return refObject.get(); } public void setImmutableObject(int age){refObject.set(new ImmutableObject(age)); }}保證共享變量的復合操作的原子性

如果是共享對象,那么我們就需要考慮在多線程環境中的原子性。如果是對共享變量的復合操作,比如:++, -- *=, /=, %=, +=, -=, <<=, >>=, >>>=, ^= 等,看起來是一個語句,但實際上是多個語句的集合。

我們需要考慮多線程下面的安全性。

考慮下面的例子:

public class CompoundOper1 { private int i=0; public int increase(){i++;return i; }}

例子中我們對int i進行累加操作。但是++實際上是由三個操作組成的:

1.從內存中讀取i的值,并寫入CPU寄存器中。

2.CPU寄存器中將i值+1

3.將值寫回內存中的i中。

如果在單線程環境中,是沒有問題的,但是在多線程環境中,因為不是原子操作,就可能會發生問題。

解決辦法有很多種,第一種就是使用synchronized關鍵字

public synchronized int increaseSync(){ i++; return i;}

第二種就是使用lock:

private final ReentrantLock reentrantLock=new ReentrantLock();public int increaseWithLock(){ try{reentrantLock.lock();i++;return i; }finally {reentrantLock.unlock(); }}

第三種就是使用Atomic原子類:

private AtomicInteger atomicInteger=new AtomicInteger(0);public int increaseWithAtomic(){ return atomicInteger.incrementAndGet();}保證多個Atomic原子類操作的原子性

如果一個方法使用了多個原子類的操作,雖然單個原子操作是原子性的,但是組合起來就不一定了。

我們看一個例子:

public class CompoundAtomic { private AtomicInteger atomicInteger1=new AtomicInteger(0); private AtomicInteger atomicInteger2=new AtomicInteger(0); public void update(){atomicInteger1.set(20);atomicInteger2.set(10); } public int get() {return atomicInteger1.get()+atomicInteger2.get(); }}

上面的例子中,我們定義了兩個AtomicInteger,并且分別在update和get操作中對兩個AtomicInteger進行操作。

雖然AtomicInteger是原子性的,但是兩個不同的AtomicInteger合并起來就不是了。在多線程操作的過程中可能會遇到問題。

同樣的,我們可以使用同步機制或者鎖來保證數據的一致性。

保證方法調用鏈的原子性

如果我們要創建一個對象的實例,而這個對象的實例是通過鏈式調用來創建的。那么我們需要保證鏈式調用的原子性。

考慮下面的一個例子:

public class ChainedMethod { private int age=0; private String name=''; private String adress=''; public ChainedMethod setAdress(String adress) {this.adress = adress;return this; } public ChainedMethod setAge(int age) {this.age = age;return this; } public ChainedMethod setName(String name) {this.name = name;return this; }}

很簡單的一個對象,我們定義了三個屬性,每次set都會返回對this的引用。

我們看下在多線程環境下面怎么調用:

ChainedMethod chainedMethod= new ChainedMethod();Thread t1 = new Thread(() -> chainedMethod.setAge(1).setAdress('www.flydean.com1').setName('name1'));t1.start();Thread t2 = new Thread(() -> chainedMethod.setAge(2).setAdress('www.flydean.com2').setName('name2'));t2.start();

因為在多線程環境下,上面的set方法可能會出現混亂的情況。

怎么解決呢?我們可以先創建一個本地的副本,這個副本因為是本地訪問的,所以是線程安全的,最后將副本拷貝給新創建的實例對象。

主要的代碼是下面樣子的:

public class ChainedMethodWithBuilder { private int age=0; private String name=''; private String adress=''; public ChainedMethodWithBuilder(Builder builder){this.adress=builder.adress;this.age=builder.age;this.name=builder.name; } public static class Builder{private int age=0;private String name='';private String adress='';public static Builder newInstance(){ return new Builder();}private Builder() {}public Builder setName(String name) { this.name = name; return this;}public Builder setAge(int age) { this.age = age; return this;}public Builder setAdress(String adress) { this.adress = adress; return this;}public ChainedMethodWithBuilder build(){ return new ChainedMethodWithBuilder(this);} }

我們看下怎么調用:

final ChainedMethodWithBuilder[] builder = new ChainedMethodWithBuilder[1];Thread t1 = new Thread(() -> { builder[0] =ChainedMethodWithBuilder.Builder.newInstance().setAge(1).setAdress('www.flydean.com1').setName('name1').build();});t1.start();Thread t2 = new Thread(() ->{ builder[0] =ChainedMethodWithBuilder.Builder.newInstance().setAge(1).setAdress('www.flydean.com1').setName('name1').build();});t2.start();

因為lambda表達式中使用的變量必須是final或者final等效的,所以我們需要構建一個final的數組。

讀寫64bits的值

在java中,64bits的long和double是被當成兩個32bits來對待的。

所以一個64bits的操作被分成了兩個32bits的操作。從而導致了原子性問題。

考慮下面的代碼:

public class LongUsage { private long i =0; public void setLong(long i){this.i=i; } public void printLong(){System.out.println('i='+i); }}

因為long的讀寫是分成兩部分進行的,如果在多線程的環境中多次調用setLong和printLong的方法,就有可能會出現問題。

解決辦法本簡單,將long或者double變量定義為volatile即可。

private volatile long i = 0;

以上就是詳解java安全編碼指南之可見性和原子性的詳細內容,更多關于java安全編碼指南之可見性和原子性的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
精品亚洲成人| 亚洲v天堂v手机在线| 午夜精品网站| 蜜臀久久精品| 久久亚洲成人| 99精品在线| 国产精品高清一区二区| 国产精品亚洲成在人线| 欧美成a人片免费观看久久五月天| 免费成人在线视频观看| 国产国产精品| 中文在线不卡| 日韩高清一级| 日韩中文字幕| 日韩av午夜在线观看| 欧美1区2区3| 精品国产精品国产偷麻豆| 精品一区二区男人吃奶| 卡一卡二国产精品| 久久久久久免费视频| 激情欧美一区二区三区| 国产中文字幕一区二区三区| 国产极品模特精品一二| 日韩啪啪电影网| 激情丁香综合| 香蕉精品999视频一区二区| 美女尤物久久精品| 美女久久精品| 免费福利视频一区二区三区| av一区在线| 丝袜美腿高跟呻吟高潮一区| 国产欧美综合一区二区三区| 精品国产乱码久久久久久1区2匹| jizzjizz中国精品麻豆| 不卡在线一区二区| 日本午夜免费一区二区| 国产免费av一区二区三区| 六月丁香综合在线视频| 你懂的国产精品| 一区福利视频| 欧美日韩一区二区高清| 精品美女在线视频| 在线一区二区三区视频| 国产精品白浆| 成人看片网站| 石原莉奈在线亚洲二区| 精品一区av| 亚洲五月婷婷| 91精品尤物| 国产在线欧美| 色8久久久久| 日本国产一区| 亚洲手机在线| 久久99精品久久久野外观看| 亚洲成av人片一区二区密柚| 亚洲精品成a人ⅴ香蕉片| 国产综合色区在线观看| 综合欧美亚洲| 国产h片在线观看| 亚洲精品第一| 麻豆精品蜜桃| 久久久精品区| 99香蕉国产精品偷在线观看| 综合视频一区| 国产麻豆久久| 久久久久黄色| 久久婷婷丁香| 欧美天堂在线| 五月婷婷亚洲| 亚洲综合在线电影| 久久国产精品免费精品3p| 亚洲二区精品| 高清一区二区| 国产在线日韩精品| 日本亚洲不卡| 午夜影院欧美| 国产一区二区高清| 黑人精品一区| 久久久9色精品国产一区二区三区| 在线视频精品| 国产精品一区二区三区美女| 97精品一区| 91精品成人| 视频一区二区三区在线| 国产欧美在线| 在线一区免费| 日韩中文首页| 国产aa精品| 日韩中文字幕在线一区| 国产综合视频| 日韩精品看片| www.com.cn成人| 欧美激情福利| 日韩av一区二区在线影视| 婷婷精品在线| 国产一区91| 久久精品日韩欧美| 国产日本亚洲| 国产精品巨作av| 亚洲精品系列| 免费人成在线不卡| 中文一区在线| 亚洲精品裸体| 日韩精品一二三| 欧美日韩国产综合网| 午夜亚洲一区| 久久亚洲欧洲| 久久成人一区| 久久香蕉精品| 亚州av日韩av| 蜜桃视频一区二区三区| 中文久久精品| 久久高清精品| 欧美一区二区三区高清视频| av资源中文在线天堂| 国产精选在线| 欧美成人午夜| 91成人精品视频| 伊人久久婷婷| 日韩在线观看中文字幕| 日韩精品免费一区二区夜夜嗨 | 欧美一区网站| 日本一区免费网站| 国产日韩欧美一区| 国产精品一区二区三区av麻| 欧美高清一区| 欧美aa国产视频| 性色一区二区| 亚洲色图网站| 日韩欧美激情| 开心激情综合| 国产精品多人| 精品无人区麻豆乱码久久久 | 日韩在线观看一区二区| 一区二区精品| 日韩欧美久久| 国产激情精品一区二区三区| 韩国精品主播一区二区在线观看| 牛牛精品成人免费视频| 欧美黑人做爰爽爽爽| 成人在线观看免费视频| 欧美不卡高清| 奶水喷射视频一区| 亚洲开心激情| 欧美91在线| 国产精品7m凸凹视频分类| 中文字幕在线视频网站| 美女av在线免费看| 香蕉成人av| 婷婷亚洲五月色综合| 人人爱人人干婷婷丁香亚洲| 国产精品资源| 日韩欧美精品一区| 亚洲www啪成人一区二区| 丝袜诱惑制服诱惑色一区在线观看| 人人爽香蕉精品| 欧美日韩99| 国产成人精品亚洲日本在线观看| 国产精品试看| 91成人在线网站| 国产v日韩v欧美v| 亚洲人成亚洲精品| 精品网站999| 欧美精品黄色| 国产日韩一区二区三免费高清| 日韩三区免费| 国产精品一区高清| 九一国产精品| 国产日韩免费| 精品国产美女a久久9999| 蜜桃久久精品一区二区| 国产一区二区三区四区大秀| 午夜欧美精品| 国产精品久久免费视频| 午夜欧美视频| 成人日韩精品| 免费久久99精品国产| 国产女人18毛片水真多18精品| 成人久久久久| 国产精品二区影院| 亚洲精品自拍| 国产视频亚洲| 欧美jjzz| 999久久久91| 国产高清亚洲| 欧美日韩中出| 91成人在线精品视频| 亚洲香蕉久久| 国产视频亚洲| 欧美.日韩.国产.一区.二区 | 欧美日韩在线二区| 精品成人18| 久久亚州av| 美女久久久久久| 免费在线亚洲欧美| 久久亚洲电影| 制服诱惑一区二区| 亚洲91精品| 国内精品99| 亚洲精品电影|