当前位置: 首页 > news >正文

厦门模板建站企业咨询公司经营范围

厦门模板建站,企业咨询公司经营范围,如何把网站做权重,基本网站建设什么是原子类#xff1f; java 1.5引进原子类#xff0c;具体在java.util.concurrent.atomic包下#xff0c;atomic包里面一共提供了13个类#xff0c;分为4种类型#xff0c;分别是#xff1a; 原子更新基本类型#xff0c;原子更新数组#xff0c;原子更新引用 java 1.5引进原子类具体在java.util.concurrent.atomic包下atomic包里面一共提供了13个类分为4种类型分别是 原子更新基本类型原子更新数组原子更新引用原子更新属性。 原子类也是java实现同步的一套解决方案。 什么时候使用原子类 当我们只是需要一个简单的、高效、线程安全的递增或者递减方案 简单操作简单底层实现j简单高效占用资源少操作速度快安全在高并发和多线程环境下要保证数据的正确性 当然这种情况可以使用synchronized关键字和lock可以实现但是代码量会上去而且性能也会降低一点所以用原子类就比较方便一点 原子变量类简单介绍 原子变量类在java.util.concurrent.atomic包下总体来看有这么多个 基本类型 AtomicBoolean布尔型AtomicInteger整型AtomicLong长整型 数组 AtomicIntegerArray数组里的整型AtomicLongArray数组里的长整型AtomicReferenceArray数组里的引用类型 引用类型 AtomicReference引用类型AtomicStampedReference带有版本号的引用类型AtomicMarkableReference带有标记位的引用类型 对象的属性 AtomicIntegerFieldUpdater原子更新对象中int类型字段的值基于反射的使用程序可以对指定类的指定的volatile int字段进行原子更新AtomicLongFieldUpdater原子更新对象中Long类型字段的值基于反射的使用程序可以对指定类的指定的volatile long字段进行原子更新AtomicReferenceFieldUpdater原子更新引用类型字段的值基于反射的使用程序可以对指定类的指定的volatile volatile引用进行原子更新 JDK8新增 Accumulator累加器 DoubleAccumulator、LongAccumulator、 Adder累加器 DoubleAdderLongAdder 是对AtomicLong等类的改进。比如LongAccumulator与LongAdder在高并发环境下比AtomicLong更高效。 Atomic包里的类基本都是使用Unsafe实现的包装类。 从原理上来说就是Atomic包的类的实现大多数都是调用的unsafe方法而unsafe底层实际上是调用C代码C代码调用汇编最后生成出一条CPU指令cmpxchg,完成操作这也是为什么CAS是原子性操作因为是一条CPU指令不会被打断。 原子变量类的使用 基本类型原子类Atomic* 这里以原子更新基本类型中的AtomicInteger类为例介绍通用的API接口和使用方法。 常用的API public final int set() //设一个值 public final int get() //获取当前的值 public final int getAndSet(int newValue)//获取当前的值并设置新的值 public final int getAndIncrement()//获取当前的值并自增 public final int getAndDecrement() //获取当前的值并自减 public final int getAndAdd(int delta) //获取当前的值并加上预期的值 boolean compareAndSet(int expect, int update) //如果当前值等于预期值则以原子方式将该值设置为输入值update public final void lazySet(int newValue) //最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。AtomicInteger a new AtomicInteger(0);for (int i 1; i 5; i) {a.getAndIncrement(); // a 自增相当于 a }//获取当前a的值System.out.println(AtomicInteger a从0自增4次结果为 a.get()); ​System.out.println(AtomicInteger 当前a为 a.getAndDecrement() 并自减一次); //a -- ​//获取当前a的值并更新a为8System.out.println(AtomicInteger a当前值为a.getAndSet(8)并更新a为8); ​//获取当前a的值并将a加6System.out.println(AtomicInteger a当前值为a.getAndAdd(6)并将a加6); ​a.compareAndSet(12,9); //如果a12就把a更新为9否则不进行操作System.out.println(AtomicInteger a当前值为a.get()); ​a.compareAndSet(14,9); //如果a14就把a更新为9否则不进行操作System.out.println(AtomicInteger a当前值为a.get()); ​AtomicInteger a从0自增4次结果为4 AtomicInteger 当前a为4并自减一次 AtomicInteger a当前值为3并更新a为8 AtomicInteger a当前值为8并将a加6 AtomicInteger a当前值为14 AtomicInteger a当前值为9 数组类型原子类(Atomic*Array) 这里以AtomicIntegerArray 为例介绍通用的API接口和使用方法。 常用API public final int get(int i) //获取 indexi 位置元素的值 public final int set(int i, int newValue) //为 indexi 位置元素设新值 public final int getAndSet(int i, int newValue) //返回 indexi 位置的当前的值并将其设置为新值newValue public final int getAndIncrement(int i) //获取 indexi 位置元素的值并让该位置的元素自增 public final int getAndDecrement(int i) //获取 indexi 位置元素的值并让该位置的元素自减 public final int getAndAdd(int i, int delta) //获取 indexi 位置元素的值并加上预期的值 boolean compareAndSet(int i, int expect, int update) //如果indexi 位置的值等于预期值则以原子方式将 indexi 位置的元素值设置为输入值update public final void lazySet(int i, int newValue) //最终 将indexi 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。基本使用 int[] a {1,1,1,1};AtomicIntegerArray arr new AtomicIntegerArray(a);System.out.println(arr数组初始值为 arr.toString()); ​for (int i 0; i 4; i) {arr.getAndIncrement(i); //index i位置上的值arr[i]自增相当于 a[i] }System.out.println(arr数组每个元素都自增1后为 arr.toString());//获取当前arr[1]的值System.out.println(arr[1]的值为 arr.get(1)); ​System.out.println(arr[2]当前值为 arr.getAndDecrement(2) 并让arr[2]自减一次); //a[2]-- ​//获取当前a[2]的值并更新a为8System.out.println(arr[2]当前值为arr.getAndSet(2,8)并更新a[2]为8); ​//获取当前a的值并将a加6System.out.println(arr[2]当前值为arr.getAndAdd(2,6)并将a[2]加6); ​arr.compareAndSet(2,12,9); //如果a[2]12就把a[2]更新为9否则不进行操作System.out.println(arr[2]当前值为arr.get(2)); ​arr.compareAndSet(2,14,9); //如果a[2]14就把a[2]更新为9否则不进行操作System.out.println(arr[2]当前值为arr.get(2));arr数组初始值为[1, 1, 1, 1] arr数组每个元素都自增1后为[2, 2, 2, 2] arr[1]的值为2 arr[2]当前值为2并让arr[2]自减一次 arr[2]当前值为1并更新a[2]为8 arr[2]当前值为8并将a[2]加6 arr[2]当前值为14 arr[2]当前值为9 引用类型原子类(Atomic*Reference) User类 package 原子类; ​ public class User {private String name;private int age; ​public User(String name, int age) {this.name name;this.age age;} ​public String getName() {return this.name;} ​public void setName(final String name) {this.name name;} ​public int getAge() {return this.age;} ​public void setAge(final int age) {this.age age;} ​Overridepublic String toString() {return User{ name name , age age };} ​ }package 原子类; ​ import java.util.concurrent.atomic.AtomicReference; ​ public class Demo3 { ​public static void main(String[] args) {AtomicReferenceUser atunew AtomicReference(); ​User user1new User(张三,10);User user2new User(李四,16);User user3new User(王五,19);atu.set(user1);//常用的API和前面的是差不多的System.out.println(atu.getAndSet(user2));System.out.println(atu.get()); ​System.out.println(atu.compareAndSet(user2,user3));System.out.println(atu.get());} //User{name张三, age10} //User{name李四, age16} //true //User{name王五, age19} ​ ​ }什么是ABA问题 ABA问题指在CAS操作过程中当一个值从A变成B又更新回A普通CAS机制会误判通过检测。这时候就可能导致程序出现意外的结果。 在高并发场景下使用CAS操作可能存在ABA问题也就是在一个值被修改之前先被其他线程修改为另外的值然后再被修改回原值此时CAS操作会认为这个值没有被修改过导致数据不一致。 如何解决 为了解决ABA问题Java中提供了AtomicStampedReference类原子标记参考该类通过使用版本号的方式来解决ABA问题。每个共享变量都会关联一个版本号CAS操作时需要同时检查值和版本号是否匹配。因此如果共享变量的值被改变了版本号也会发生变化即使共享变量被改回原来的值版本号也不同因此CAS操作会失败。 AtomicStampedReferenceV解决ABA问题 带版本号的引用类型原子类可以解决ABA问题 AtomicStampedReference在构建的时候需要一个类似于版本号的int类型变量stamped每一次针对共享数据的变化都会导致该 stamped 的变化stamped 需要应用程序自身去负责AtomicStampedReference并不提供一般使用时间戳作为版本号因此就可以避免ABA问题的出现AtomicStampedReference的使用也是极其简单的创建时我们不仅需要指定初始值还需要设定stamped的初始值在AtomicStampedReference的内部会将这两个变量封装成Pair对象 package 原子类; import 原子类.User; import java.util.concurrent.atomic.AtomicStampedReference; class ABADemo {public static void main(String[] args) {User zs new User(张三,18);User ls new User(李四,25);//创建对象带版本号的引用类型原子类添加对象和初始化的版本号AtomicStampedReferenceUser reference new AtomicStampedReference(zs, 1); ​new Thread(() - {int stamp reference.getStamp();User referenceUser reference.getReference();// 保证线程t2可以拿到版本号try {Thread.sleep(1000L);} catch (InterruptedException e) {e.printStackTrace();}// ASystem.out.println(Thread.currentThread().getName() 版本号: stamp 对象 referenceUser);// Bboolean compareAndSet reference.compareAndSet(referenceUser, ls, stamp, stamp 1);System.out.println(Thread.currentThread().getName() 将数据设置 (compareAndSet?成功 :失败 ) reference.getReference());// AcompareAndSet reference.compareAndSet(reference.getReference(),zs,reference.getStamp(),reference.getStamp() 1);System.out.println(Thread.currentThread().getName() 将数据设置 (compareAndSet?成功 :失败 ) reference.getReference()); ​},t1).start(); ​new Thread(() - {int stamp reference.getStamp();User referenceUser reference.getReference();// 保证线程t1发生完ABA问题try {Thread.sleep(5000L);} catch (InterruptedException e) {e.printStackTrace();}boolean compareAndSet reference.compareAndSet(referenceUser,ls,stamp,stamp 1);System.out.println(Thread.currentThread().getName() 将数据设置 (compareAndSet?成功 :失败 ) reference.getReference()); ​},t2).start();} }t1版本号:1 对象User{name张三, age18} t1将数据设置成功 User{name李四, age25} t1将数据设置成功 User{name张三, age18} t2将数据设置失败 User{name张三, age18} 常用API // 构造函数初始化引用和版本号 public AtomicStampedReference(V initialRef, int initialStamp)// 以原子方式获取当前引用值 public V getReference()// 以原子方式获取当前版本号 public int getStamp()// 以原子方式获取当前引用值和版本号 public V get(int[] stampHolder)// 以原子的方式同时更新引用值和版本号 // 当期望引用值不等于当前引用值时操作失败返回false // 当期望版本号不等于当前版本号时操作失败返回false // 在期望引用值和期望版本号同时等于当前值的前提下 // 当新的引用值和新的版本号同时等于当前值时不更新直接返回true // 当新的引用值和新的版本号不同时等于当前值时同时设置新的引用值和新的版本号返回true public boolean weakCompareAndSet(V  expectedReference,V  newReference,int expectedStamp,int newStamp)// 以原子的方式同时更新引用值和版本号 // 当期望引用值不等于当前引用值时操作失败返回false // 当期望版本号不等于当前版本号时操作失败返回false // 在期望引用值和期望版本号同时等于当前值的前提下 // 当新的引用值和新的版本号同时等于当前值时不更新直接返回true // 当新的引用值和新的版本号不同时等于当前值时同时设置新的引用值和新的版本号返回true public boolean compareAndSet(V   expectedReference,V   newReference,int expectedStamp,int newStamp)// 以原子方式设置引用的当前值为新值newReference // 同时以原子方式设置版本号的当前值为新值newStamp // 新引用值和新版本号只要有一个跟当前值不一样就进行更新 public void set(V newReference, int newStamp)// 以原子方式设置版本号为新的值 // 前提引用值保持不变 // 当期望的引用值与当前引用值不相同时操作失败返回fasle // 当期望的引用值与当前引用值相同时操作成功返回true public boolean attemptStamp(V expectedReference, int newStamp)// 使用sun.misc.Unsafe类原子地交换两个对象 private boolean casPair(PairV cmp, PairV val) ​AtomicMarkableReferenceV 状态戳简化 AtomicMarkableReference与AtomicStampedReference的区别是Pair内部类维护的类型不同。 类似于上面的版本号但是主要是解决一次性问题 解决是否修改过它的定义就是将状态戳简化boolean也就是true或者falset类似于一次性筷子 static AtomicMarkableReferenceInteger markableReference new AtomicMarkableReference(100,false);public static void main(String[] args) {new Thread(()-{boolean marked markableReference.isMarked();System.out.println(Thread.currentThread().getName()\t默认标识marked);//暂停1秒钟线程等待后面的T2线程和我拿到一样的模式flag标识都是falsetry {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}markableReference.compareAndSet(100, 1000, marked, !marked);},t1).start();new Thread(()-{boolean marked markableReference.isMarked();System.out.println(Thread.currentThread().getName()\t默认标识marked);//这里停2秒让t1先修改,然后t2试着修改try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}boolean t2Result markableReference.compareAndSet(100, 1000, marked, !marked);System.out.println(Thread.currentThread().getName()\tt2线程result--t2Result);System.out.println(Thread.currentThread().getName()\tmarkableReference.isMarked());System.out.println(Thread.currentThread().getName()\tmarkableReference.getReference());},t2).start();} } ​运行结果 //t1 默认标识false //t2 默认标识false //t2 t2线程result--false //t2 true //t2 1000 对象的属性修改原子类 使用要求 更新的对象属性必须使用public volatile修饰符因为对象的属性修改类型原子类都是抽象类所以每次使用都必须使用静态方法newUpdater()创建一个更新器并且需要设置想要更新的类和属性。属性的修饰符public/protected/default/private要保证当前操作对该属性可以直接进行比如当我们用private volatile int age 时就会报错因为private修饰时外部无法访问也无法修改。只能是实例变量不能是类变量也就是说不能加static关键字。只能是可修改变量不能使final变量因为final的语义就是不可修改。对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段不能修改其包装类型Integer/Long 。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。 修改引用类型 package 原子类; ​ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; ​ class yingyongDemo {public static void main(String[] args) {MyVar myVar new MyVar();for (int i 1; i 5; i) {new Thread(() - {myVar.init();}, String.valueOf(i)).start(); ​}} ​public static class MyVar {public volatile Boolean isInit Boolean.FALSE;AtomicReferenceFieldUpdaterMyVar, Boolean referenceFieldUpdater AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, isInit); ​public void init() {if (referenceFieldUpdater.compareAndSet(this, Boolean.FALSE, Boolean.TRUE)) {System.out.println(Thread.currentThread().getName() \t -----start init,needs 3 seconds);try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() \t -----over init);} else {System.out.println(Thread.currentThread().getName() \t 抱歉已经有其他线程进行了初始化);}}} }1 -----start init,needs 3 seconds 5 抱歉已经有其他线程进行了初始化 4 抱歉已经有其他线程进行了初始化 2 抱歉已经有其他线程进行了初始化 3 抱歉已经有其他线程进行了初始化 1 -----over init Adder累加器 LogAdder比AtomicLog的区别 java8引入的相比较是一个比较新的类 高并发下LogAdder比AtomicLog效率高不过本质是空间换时间 竞争激烈的时候LongAdder把不同线程对应到不同的Cell上进行修改降低了冲突的概率是多段锁的理念提高了并发性 LongAdder适合的场景是统计求和计数的场景而且LongAdder基本只提供了add方法而AtomicLong还具有cas方法 多线程下AtomicLong的性能有20个线程对同一个AtomicLong累加由于竞争很激烈每一次加法都要flush和refresh,导致很耗费资源 在内部这个LongAdder的实现原理和AtomicLong是不同的刚才的AtomicLong的实现原理是每一次加法都需要做同步所以在高并发的时候会导致冲突比较多也就降低了效率 而此时的LongAdder,每个线程都有一个计数器仅用来在自己的线程内计算这样一来就不会和其他线程的计数器干扰 如下图第一个线程的计数器的数值也就是ctr’,为1的时候可能线程2的计数器ctr’’的数值已经是3了他们之间并不存在竞争关系所以在加和的过程中根本不需要同步机制也不需要刚才的flush和reflush。这里没有一个公共的counter来给所有线程统一计数 LongAdder引入了分段累加的概念内部有一个base变量和一个Cell[]数组共同参与计数 base变量竞争不激烈直接累加到该变量上 Cell[]:竞争激烈各个线程分散累加到自己的槽Cell[i]中 总的来说 LongAdder的基本思路就是分散热点 将value值分散到一个Cell数组中不同线程会命中到数组的不同槽中各个线程只对自己槽中的那个值进行CAS操作这样热点就被分散了冲突的概率就小很多。如果要获取真正的long值只要将各个槽中的变量值累加返回。sum源码 public long sum() {Cell[] as cells; Cell a;long sum base;if (as ! null) {for (int i 0; i as.length; i) {if ((a as[i]) ! null)sum a.value;}}return sum;} ​sum()会将所有Cell数组中的value和base累加作为返回值 核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去从而降级更新热点 。 一句话总结longAdder原理 LongAdder在无竞争的情况跟AtomicLong一样对同一个base进行操作当出现竞争关系时则是采用化整为零的做法从空间换时间用一个数组 将一个value拆分进这个数组cells。 多个线程需要同时对value进行操作时候可以对线程id进行hash得到hash值再根据hash值映射到这个数组cells的某个下标再对该下标所对应的值进行自增操作。当所有线程操作完毕将数组cells的所有值和无竞争值base都加起来作为最终结果。 与AtomicLong对比 名称原理场景缺陷AtomicLongCAS 自旋低并发下的全局计算AlomicLong能保证并发情况下计数的准确性其内部通过CAS来解决并发安全性的问题。可允许一些性能损耗要求高精度时可使用。AtomicLong是多个线程针对单个热点值value进行原子操作高并发后性能急剧下降。N个线程CAS操作修改线程的值每次只有一个成功过其它N-1失败失败的不停的自旋直到成功这样大量失败自旋的情况占用大量CPULongAdderCASBaseCell数组分散通过空间换时间分散了热点数据高并发下的全局计算当需要在高并发下有较好的性能表现且对值的精确度要求不高时可以使用。LongAdder是每个线程拥有自己的槽各个线程一般只对自己槽中的那个值进行CAS操作sum求和后还有计算线程修改结果的话最后结果不够准确 Accumulator累加器 public class LongAccumulatorDemo {public static void main(String[] args) {//需要传入累加的函数LongAccumulator accumulator new LongAccumulator((x,y)-xy,0);ExecutorService executor Executors.newFixedThreadPool(8);IntStream.range(1,10).forEach(i-executor.submit(()-accumulator.accumulate(i)));executor.shutdown();while (!executor.isTerminated()){ ​}System.out.println(accumulator.getThenReset()); //45} } ​使用场景 ① 适用于需要大量计算并且需要并行计算的场景如果不需要并行计算可用for循环解决问题用了Accumulator累加器可利用多核同时计算提供效率 ② 计算的顺序不能成为瓶颈线程1可能在线程5之后运行也可能在之前运行不影响最终结果
http://www.zqtcl.cn/news/96086/

相关文章:

  • 柳州做网站的企业做黑彩网站
  • 商城网站开发那家好网站建设知识平台
  • 莱州网站定制flash网站cms
  • 经营范围里的网站建设直播系统程序
  • 58同城类似的网站开发wordpress 地方生活
  • wordpress 七牛ossseo系统
  • 郑州做网站 熊掌号太原今天最新通知
  • 文章网站如何与压力做足球比赛直播间在线观看
  • 越秀网站建设优化呼和浩特住房和城乡建设部网站
  • 河南省路桥建设集团网站建网站公司郑州
  • 海沧做网站深圳外贸招聘
  • 网站建设置顶多少钱翻译成英文
  • 柳州正规网站制作公司哪家好怎么学好网站建设
  • 德宏做网站网站的设计思路范文
  • 自己的电脑做网站服务器深圳福田有什么好玩的地方
  • 奕腾网站建设上海十大装修公司排名榜单
  • 简述建设一个网站的基本步骤wordpress欢迎新会员
  • 国外医疗网站模板wordpress主题 科技
  • 海淀企业型网站建设wordpress自定义帖子链接
  • 自己的网站怎么优化做网页的
  • dw设计一个简单网站网页微信版文件传输
  • 网站地图怎么做XML宁波网站建设服务提供商
  • 中石化两学一做网站获取网站域名
  • 吉林长春火车站官网湖北葛洲坝建设工程网站
  • 重庆网站推广服务广告公司女员工深夜兼职
  • 网站的要素是什么wordpress框架解密_day3
  • 抽奖怎么做网站彩页设计公司
  • 推广网站文案素材lamp环境wordpress
  • 合肥网站建设公司 推荐百度下载安装2021
  • 沈阳网站备案照相离婚证app制作软件