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

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

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

瀏覽:157日期: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久久,三上悠亚国产精品一区二区三区
精品视频免费| 欧美日韩免费观看视频| av高清不卡| 国产精品色在线网站| 一级成人国产| 亚洲乱亚洲高清| 中文一区二区| 亚洲成人精选| 欧美日韩尤物久久| 久久婷婷丁香| 深夜视频一区二区| 国产拍在线视频| 精品亚洲免a| 国产精品久久久网站| 欧美亚洲一区二区三区| 亚洲三级国产| 亚洲国产日韩欧美在线| 你懂的国产精品永久在线| 日韩精品午夜视频| 国产精品夜夜夜| 国产精品一区二区三区美女 | 日韩精品亚洲aⅴ在线影院| 中文字幕免费精品| 亚洲精品人人| 日韩美女国产精品| 亚洲专区视频| 日本不卡不码高清免费观看 | 欧美国产极品| 日韩成人a**站| 精品视频免费| 国产精品久久久久蜜臀 | 国产精品久久久久蜜臀| 国产成人77亚洲精品www| 国产成人久久精品一区二区三区| 久久不卡日韩美女| 国产精品密蕾丝视频下载| 久久中文字幕一区二区三区| 日本久久综合| 中文字幕系列一区| 日韩免费久久| 91精品1区| 综合国产在线| 国产亚洲人成a在线v网站| 久久一区精品| 亚洲激情中文在线| 亚洲精品一级| 国产精品久久久久久久久久妞妞 | 亚洲精品第一| 日韩精品中文字幕吗一区二区| 国产精品对白久久久久粗| 精品资源在线| 久久高清免费| 日韩高清不卡在线| 国产精品视频一区二区三区四蜜臂| 精品一区二区三区的国产在线观看 | 日韩一级精品| 婷婷五月色综合香五月| 国产精品免费99久久久| 日韩电影免费网址| 在线国产日韩| 欧美国产日韩电影| 欧美羞羞视频| 视频一区免费在线观看| 国产精品**亚洲精品| 日韩精品永久网址| 蜜臀精品一区二区三区在线观看 | 日本一区二区三区中文字幕| sm捆绑调教国产免费网站在线观看 | 精品一区视频| 午夜av一区| 麻豆国产精品一区二区三区| 91精品一区二区三区综合| 免费成人网www| 国产另类在线| 精品中文字幕一区二区三区av| 日韩欧美高清一区二区三区| 激情六月综合| 国产欧美午夜| 九色porny丨国产首页在线| 免费在线观看日韩欧美| 国产一区一一区高清不卡| 在线亚洲激情| 国产成人精品一区二区三区视频| 好吊日精品视频| 亚洲精品亚洲人成在线观看| 日韩欧美网址| 国产欧美日韩在线一区二区| 在线精品小视频| 欧美激情国产在线| 国产精品亚洲一区二区在线观看 | 国产情侣久久| 最新国产精品| 欧美日韩国产综合网| 日韩欧美1区| 精品国产一区二区三区av片| 日本视频一区二区| 亚洲一区欧美| 国产亚洲精品v| 婷婷综合五月| 久久久久国产精品一区三寸| 成人日韩av| а√天堂8资源在线| 精品久久影院| 麻豆一区在线| 久久国产三级| 日本午夜精品久久久久| 中文字幕一区二区av| 蜜臀av性久久久久蜜臀aⅴ四虎| 九九综合九九| 午夜久久美女| 日韩一级欧洲| 亚洲欧美视频一区二区三区| 夜久久久久久| 午夜在线精品偷拍| 亚洲一区二区成人| 尤物在线精品| 亚洲精华国产欧美| 国产亚洲亚洲| 亚洲午夜免费| 日韩免费精品| 国产亚洲一区| 国产精品欧美三级在线观看| 国产精品久久久免费| 国产精品成人**免费视频 | 91福利精品在线观看| 日韩精品导航| 日本在线不卡视频一二三区| 日韩精品一区二区三区中文字幕| 亚洲欧美日本国产专区一区| 视频一区在线视频| 日精品一区二区三区| 欧美在线看片| 精品亚洲自拍| 日韩毛片在线| 欧美日韩国产传媒| 亚洲欧美日韩视频二区| 蜜桃久久av一区| 男女男精品网站| 日本a口亚洲| 麻豆一区二区99久久久久| 97欧美在线视频| 美女毛片一区二区三区四区| 欧美专区一区二区三区| 另类av一区二区| 日韩高清一区| 欧美韩一区二区| 精品国产第一福利网站| 婷婷激情综合| 最近国产精品视频| 国产精品久久久免费| 欧美成人a交片免费看| 韩日一区二区三区| 一区二区三区四区日韩| 国产精选久久| 色88888久久久久久影院| 亚洲精品一区二区在线看| 日韩在线麻豆| 久久精品国产亚洲aⅴ| 日韩一区自拍| 久久亚洲美女| 国产精品夜夜夜| 色88888久久久久久影院| 视频在线观看一区| 国产精品夜夜夜| 久久国产影院| 日本va欧美va瓶| 亚洲永久av| 香蕉久久一区| 高清不卡一区| 久久成人亚洲| 久久尤物视频| 午夜在线视频观看日韩17c| 欧美精品不卡| 欧美日韩国产亚洲一区| 国产乱人伦精品一区| 亚洲一级高清| 久久国产精品色av免费看| 成人小电影网站| 美国三级日本三级久久99| 久久这里只有精品一区二区| 亚洲经典在线| 久久尤物视频| 日韩制服丝袜av| 精品视频在线一区二区在线| 亚洲黄页一区| 国产福利亚洲| 91精品1区| 国产在线视频欧美一区| 国产农村妇女精品一二区| 蜜桃久久久久| 免费在线观看精品| 日本在线啊啊| 日韩激情一区二区| 欧美日韩在线播放视频| 国产精品一区二区精品视频观看| 亚洲精品中文字幕乱码| 美女在线视频一区| 中文字幕日韩高清在线| 精品成人免费一区二区在线播放| 91成人在线精品视频|