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

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

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

瀏覽:60日期: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
相關文章:
主站蜘蛛池模板: 心肺复苏模拟人|医学模型|急救护理模型|医学教学模型上海康人医学仪器设备有限公司 | 通信天线厂家_室分八木天线_对数周期天线_天线加工厂_林创天线源头厂家 | jrs高清nba(无插件)直播-jrs直播低调看直播-jrs直播nba-jrs直播 上海地磅秤|电子地上衡|防爆地磅_上海地磅秤厂家–越衡称重 | 山东商品混凝土搅拌楼-环保型搅拌站-拌合站-分体仓-搅拌机厂家-天宇 | 百度爱采购运营研究社社群-店铺托管-爱采购代运营-良言多米网络公司 | 智慧物联网行业一站式解决方案提供商-北京东成基业 | 拼装地板,悬浮地板厂家,悬浮式拼装运动地板-石家庄博超地板科技有限公司 | 济南拼接屏_山东液晶拼接屏_济南LED显示屏—维康国际官网 | 活性氧化铝球|氧化铝干燥剂|分子筛干燥剂|氢氧化铝粉-淄博同心材料有限公司 | 成都热收缩包装机_袖口式膜包机_高速塑封机价格_全自动封切机器_大型套膜机厂家 | 无锡装修装潢公司,口碑好的装饰装修公司-无锡索美装饰设计工程有限公司 | 世纪豪门官网 世纪豪门集成吊顶加盟电话 世纪豪门售后电话 | 开云(中国)Kaiyun·官方网站 - 登录入口 | 高压无油空压机_无油水润滑空压机_水润滑无油螺杆空压机_无油空压机厂家-科普柯超滤(广东)节能科技有限公司 | 天津暖气片厂家_钢制散热器_天津铜铝复合暖气片_维尼罗散热器 | 环氧铁红防锈漆_环氧漆_无溶剂环氧涂料_环氧防腐漆-华川涂料 | 环压强度试验机-拉链拉力试验机-上海倾技仪器仪表科技有限公司 | 滤芯,过滤器,滤油机,贺德克滤芯,精密滤芯_新乡市宇清流体净化技术有限公司 | 内六角扳手「厂家」-温州市威豪五金工具有限公司 | 模切之家-专注服务模切行业的B2B平台! | 浙江华锤电器有限公司_地磅称重设备_防作弊地磅_浙江地磅售后维修_无人值守扫码过磅系统_浙江源头地磅厂家_浙江工厂直营地磅 | 德国EA可编程直流电源_电子负载,中国台湾固纬直流电源_交流电源-苏州展文电子科技有限公司 | lcd条形屏-液晶长条屏-户外广告屏-条形智能显示屏-深圳市条形智能电子有限公司 | 基业箱_环网柜_配电柜厂家_开关柜厂家_开关断路器-东莞基业电气设备有限公司 | 滤芯,过滤器,滤油机,贺德克滤芯,精密滤芯_新乡市宇清流体净化技术有限公司 | 闪蒸干燥机-喷雾干燥机-带式干燥机-桨叶干燥机-[常州佳一干燥设备] | 防勒索软件_数据防泄密_Trellix(原McAfee)核心代理商_Trellix(原Fireeye)售后-广州文智信息科技有限公司 | 玻璃钢型材-玻璃钢风管-玻璃钢管道,生产厂家-[江苏欧升玻璃钢制造有限公司] | 权威废金属|废塑料|废纸|废铜|废钢价格|再生资源回收行情报价中心-中废网 | 泉州陶瓷pc砖_园林景观砖厂家_石英砖地铺石价格 _福建暴风石英砖 | 气胀轴|气涨轴|安全夹头|安全卡盘|伺服纠偏系统厂家-天机传动 | 重庆波纹管|重庆钢带管|重庆塑钢管|重庆联进管道有限公司 | 健康管理师报考条件,考试时间,报名入口—首页 | 智成电子深圳tdk一级代理-提供TDK电容电感贴片蜂鸣器磁芯lambda电源代理经销,TDK代理商有哪些TDK一级代理商排名查询。-深圳tdk一级代理 | HYDAC过滤器,HYDAC滤芯,现货ATOS油泵,ATOS比例阀-东莞市广联自动化科技有限公司 | 【甲方装饰】合肥工装公司-合肥装修设计公司,专业从事安徽办公室、店面、售楼部、餐饮店、厂房装修设计服务 | 儿童乐园|游乐场|淘气堡招商加盟|室内儿童游乐园配套设备|生产厂家|开心哈乐儿童乐园 | 工业车间焊接-整体|集中除尘设备-激光|等离子切割机配套除尘-粉尘烟尘净化治理厂家-山东美蓝环保科技有限公司 | 不锈钢列管式冷凝器,换热器厂家-无锡飞尔诺环境工程有限公司 | 无线对讲-无线对讲系统解决方案-重庆畅博通信 | 食品机械专用传感器-落料放大器-低价接近开关-菲德自控技术(天津)有限公司 |