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);
@Test
public 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

并发包 concurrent(一) Atomic的更多相关文章

  1. Java并发包concurrent——ConcurrentHashMap

    转: Java并发包concurrent——ConcurrentHashMap 2018年07月19日 20:43:23 Bill_Xiang_ 阅读数 16390更多 所属专栏: Java Conc ...

  2. java并发包分析之———Atomic类型

    一.何谓Atomic?   Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过 ...

  3. 并发包学习之-atomic包

    一,模拟并发代码: 线程不安全的代码 //并发模拟代码 public class CountExample { //请求总数 public static int clientTotal = 5000; ...

  4. Java并发包concurrent类简析

    1.ConcurrentHashMap ConcurrentHashMap是线程安全的HashMap的实现. 1)添加 put(Object key , Object value) Concurren ...

  5. Concurrent初探 --- Atomic 无锁

    一.CAS算法 Compare And Swap,CAS算法的过程是这样:它包含3个参数CAS(V,E,N).V表示要更新的变量,E表示预期值,N表示新值.仅当V值等于E值时,才会将V的值设为N,如果 ...

  6. Java并发包——Atomic操作

    Java并发包——Atomic操作 摘要:本文主要学习了Java并发包下的atomic包中有关原子操作的一些类. 部分内容来自以下博客: https://blog.csdn.net/qq_303796 ...

  7. Java并发包源码分析

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

  8. java.util.concurrent包

    在JavaSE5中,JUC(java.util.concurrent)包出现了 在java.util.concurrent包及其子包中,有了很多好玩的新东西: 1.执行器的概念和线程池的实现.Exec ...

  9. 和朱晔一起复习Java并发(四):Atomic

    本节我们来研究下并发包中的Atomic类型. AtomicXXX和XXXAdder以及XXXAccumulator性能测试 先来一把性能测试,对比一下AtomicLong(1.5出来的).LongAd ...

随机推荐

  1. python 反射、md5加密

    一.issubclass,type,isinstance 1.issubclass :判断xx类是否是yyy类型(包括子类),用于类之间的判定 class GrandF: pass class Fat ...

  2. Announcing the Updated NGINX and NGINX Plus Plug‑In for New Relic (Version 2)

    In March, 2013 we released the first version of the “nginx web server” plug‑in for New Relic monitor ...

  3. Spring获取实现某接口的所有实例bean

    1.获取 applicationContext,通过ApplicationAware自动注入 2.getBeansOfType.getBeanNamesForType Map<String, I ...

  4. maven 标签classifier 研究一下

    研究一下maven的标签: <dependency> <groupId>io.netty</groupId> <artifactId>netty-tcn ...

  5. json_encode里面经常用到的 JSON_UNESCAPED_UNICODE和JSON_UNESCAPED_SLASHES

    php格式化json的函数json_encode($value,$options) 其中有2个比较常用到的参数 JSON_UNESCAPED_UNICODE(中文不转为unicode ,对应的数字 2 ...

  6. pytorch visdom可视化工具学习—1—详细使用-3-Generic Plots和Others

    4)Generic Plots 注意,服务器API遵循数据和布局对象的规则,这样您就可以生成自己的任意Plotly可视化: # Arbitrary visdom content trace = dic ...

  7. mysql之grant权限说明

    mysql中给一个用户授权如select,insert,update,delete等其中的一个或者多个权限,主要使用grant命令,格式为: 给没有用户授权 grant 权限 on 数据库对象 to ...

  8. JS上传文件、导入文件

    //开始导入 function Import() { var filepath = $('#txtUpload').val(); //校验是否选择表格 if (filepath == '') { $( ...

  9. [Micropython]TPYBoard v102 DIY照相机

    摄像头(CAMERA或WEBCAM)又称为电脑相机.电脑眼.电子眼等,是一种视频输入设备,被广泛的运用于视频会议,安防系统 .图像采集系统. 环境监控 .工业现场过程控制 等方面.本实验用TPYBoa ...

  10. macOS10.4后的刻盘新姿势

    先sudo -s 输入密码 然后终端拖入createinstallmedia   (在macOS Mojave.app显示包内容里面的resources里面的文件复制下来即可) 输入 --volume ...