1:基础概念

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

原子操作:所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程),是不需要synchronized,通常情况下,在Java里面,++i或者--i不是线程安全的,这里面有三个独立的操作:获得变量当前值,为该值+1/-1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作是“原子性”的。

例如下面的例子 IncrementAndGet类中的next()方法是一个自增1操作,为了保证线程安全加了synchronized关键字

public class IncrementAndGet() {private int value;public synchronized int next(){return value++;}
}

这种加锁的方式属于悲观锁的方式,效率太低。可以采用例外一种方式处理:

1. 从内存中读取value 值,假设为10, 我们把这个值称为A

2. B = A+1 得到 B = 11

3. 用A 和 内存的值相比, 如果相等(就是说在过去的一段时间,没人修改内存的值), 那就把B的值(11)写入内存,  如果不相等(就是说过去的一段时间, 有人修改了内存value 的值), 意味着A已经不是最新值了, 那就放弃这次修改, 跳回第1步去”

第三步其实就是一条硬件指令,保证原子执行。 在单个CPU上就不用说了,如果是有多个CPU, 这个指令甚至会锁住总线, 确保同一时刻只有一个CPU能访问内存!

final AtomicInteger value = new AtomicInteger(10);@Testpublic final int test1(){for(;;){int current = value.get();//获取当前值  int next = current+1; //设置期望值if(value.compareAndSet(current, next)){return next;}}}

AtomicInteger类compareAndSet通过原子操作实现了CAS操作,最底层基于汇编语言实现。

CAS是Compare And Set的一个简称,如下理解:

1,已知当前内存里面的值current和预期要修改成的值new传入

2,内存中AtomicInteger对象地址对应的真实值(因为有可能别修改)real与current对比,

相等表示real未被修改过,是“安全”的,将new赋给real结束然后返回;不相等说明real已经被修改,结束并重新执行1直到修改成功

我们仔细地审视这段代码, 它根本没有加锁, 其他线程都可以进入next()方法, 读取数据,操作数据, 最后使用CAS来决定这次操作是否有效, 如果内存值被别人改过,那就再次循环尝试,这就采用了乐观锁的方式。

为了说明AtomicInteger的原子性,这里代码演示多线程对一个int值进行自增操作,最后输出结果,代码如下:

public class AtomicIntegerDemo {private static AtomicInteger atomicInteger = new AtomicInteger(0);public static void main(String[] args){for (int i = 0; i < 5; i++){new Thread(new Runnable() {public void run() {//调用AtomicInteger的getAndIncement返回的是增加之前的值
                    System.out.println(atomicInteger.getAndIncrement());}}).start();}System.out.println(atomicInteger.get());}
}

输出结果如下:

0
2
1
3
4
4

原子更新数组

通过原子更新数组里的某个元素,共有3个类:

  • AtomicIntegerArray:原子更新整型数组的某个元素
  • AtomicLongArray:原子更新长整型数组的某个元素
  • AtomicReferenceArray:原子更新引用类型数组的某个元素

AtomicIntegerArray常用的方法有:

  • int getAndSet(int i, int delta):以原子方式将输入值与数组中索引为i的元素相加
  • boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值
public class AtomicIntegerArrayDemo {static int[] value = new int[]{1, 2};static AtomicIntegerArray ai = new AtomicIntegerArray(value);public static void main(String[] args){ai.getAndSet(0,3);System.out.println(ai.get(0));System.out.println(value[0]);}

运行结果是:

  3 
  1

数组value通过构造的方式传入AtomicIntegerArray中,实际上AtomicIntegerArray会将当前数组拷贝一份,所以在数组拷贝的操作不影响原数组的值。

原子更新引用类型

需要更新引用类型往往涉及多个变量,早atomic包有三个类:

  • AtomicReference:原子更新引用类型
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段
  • AtomicMarkableReference:原子更新带有标记位的引用类型。

下面以AtomicReference为例进行说明:

public class AtomicReferenceDemo {static class User{private String name;private int id;public User(String name, int id) {this.name = name;this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}}public static AtomicReference<User> ar = new AtomicReference<User>();public static void main(String[] args){User user = new User("aa",11);ar.set(user);User newUser = new User("bb",22);ar.compareAndSet(user,newUser);System.out.println(ar.get().getName());System.out.println(ar.get().getId());}
}

运行结果为:

bb 
22

可以看到user被成功更新。

原子更新字段类

如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类:

  • AtomicIntegerFieldUpdater:原子更新整型字段
  • AtomicLongFieldUpdater:原子更新长整型字段
  • AtomicStampedReference:原子更新带有版本号的引用类型。

要想原子更新字段,需要两个步骤:

  1. 每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段
  2. 更新类的字段(属性)必须为public volatile

下面的代码演示如何使用原子更新字段类更新字段:

public class AtomicIntegerFieldUpdaterDemo {//创建一个原子更新器private static AtomicIntegerFieldUpdater<User> atomicIntegerFieldUpdater =AtomicIntegerFieldUpdater.newUpdater(User.class,"old");public static void main(String[] args){User user = new User("Tom",15);//原来的年龄
        System.out.println(atomicIntegerFieldUpdater.getAndIncrement(user));//现在的年龄
        System.out.println(atomicIntegerFieldUpdater.get(user));}static class User{private String name;public volatile int old;public User(String name, int old) {this.name = name;this.old = old;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getOld() {return old;}public void setOld(int old) {this.old = old;}}
}

输出的结果如下:

15 
16

转载于:https://www.cnblogs.com/myseries/p/8627274.html

并发包 concurrent(一) Atomic相关推荐

  1. Concurrent集合 Atomic类

    Block Queue的意思就是说当一个线程调用getTask方法的时候会让线程进入等待状态直到条件满足,线程被唤醒以后,getTask方法才会被返回 java.util.concurrent包里面就 ...

  2. Java并发包concurrent——ConcurrentHashMap

    目录 1. ConcurrentHashMap的实现--JDK7版本 1.1 分段锁机制 1.2 ConcurrentHashMap的数据结构 1.3 ConcurrentHashMap的初始化 1. ...

  3. 关于JCU并发包中的Atomic原子类及其CAS

    希望造成叫醒你的是梦想 而不是闹钟 目录 类型 常用方法 CAS CAS与Atomic原子类 自旋锁 CAS缺点 Atomic原子类: 在化学中,原子是构成一般物质的最小单位,是不可分割的.而在这里, ...

  4. java并发包concurrent,大厂直通车!

    基本概念面试题集( Spring 相关概念梳理) 谈谈对Spring loC的理解? 谈谈对Spring Dl的理解? BeanFactory 接口和ApplicationContext接口不同点是什 ...

  5. Java并发编程,无锁CAS与Unsafe类及其并发包Atomic

    为什么80%的码农都做不了架构师?>>>    我们曾经详谈过有锁并发的典型代表synchronized关键字,通过该关键字可以控制并发执行过程中有且只有一个线程可以访问共享资源,其 ...

  6. Java并发编程-无锁CAS与Unsafe类及其并发包Atomic

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772470 出自[zejian ...

  7. 并发编程之并发包详解

    并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善.现代的PC都有多个CPU或一个CPU中有多个 ...

  8. 掌握Volatile关键字及其牵扯的JUC并发包

    熬不过的日子就让自己忙碌起来 目录 Volatile三大特性 保证可见性 JMM内存模型 原理 不保证原子性 原因 Atomic原子类 禁止指令重排 内存屏障 应用场景 状态标识 一次性安全发布 独立 ...

  9. 微服务接口限流的设计与思考(附GitHub框架源码)

    http://www.infoq.com/cn/articles/microservice-interface-rate-limit?useSponsorshipSuggestions=true&am ...

最新文章

  1. 萤石云 服务器错误 10017
  2. activemq无账户密码登录配置修改
  3. EM算法(Expectation Maximization Algorithm)详解
  4. HDU2553 N皇后问题
  5. 文件上传常见绕过分析
  6. python获取window共享目录列表_利用Python获取DICOM RTstructure勾画列表
  7. Ubuntu配置java环境
  8. 正常web页面登录时效是多少_Web 系统的安全性测试之权限管理测试
  9. 【转】jquery ui中文说明(使用方法)
  10. 重启nginx后丢失nginx.pid的解决方法
  11. 使用IBM WID 建立SOA 之WID简介
  12. PCB设计中电源与地之间电容的作用(具体放置面积, 大小等等)
  13. JQuery——基础
  14. html视图查看,视图.html · zhaohaihang/Semantic UI demo - Gitee.com
  15. 代号红狗:那些站在微软云起点的中国创业者
  16. mysql连接工具_连接MySQL常用工具
  17. 11 个超火的前端必备在线工具,终于有时间上班摸鱼了
  18. 处理24人!又一批科研不端案例被通报批评
  19. windows保护无法启动修复服务器,win10系统使用“sfc /scannow”修复系统提示Windows资源保护无法启动修复服务怎么办...
  20. 【回顾】基于树莓派下的4G模块(EC200U-CN)实现远程控制短信的收发

热门文章

  1. 对自己的python项目配置PYTHONPATH
  2. 第10课 skymvc 企业网站制作
  3. 7.Git的版本退回
  4. 与一线Linux嵌入式开发工程师的对话
  5. 【Oracle】-【LRU和DBWR】-LRU算法与DBWR中的应用
  6. 利用OpenIPMI监控服务器温度
  7. 新漏洞可导致攻击者劫持Kindle
  8. ZDI 公布2020年 Pwn2Own 东京赛规则和奖金
  9. RCNN,fast R-CNN,faster R-CNN
  10. 《剑指offer》——基础数据结构:从简单知识构建细致扎实的思考和实现能力...