无锁机制下的原子性操作

  通常使用volatile关键字修饰字段可以实现多个线程的可见性和读写的原子性,但是对于字段的复杂性操作就需要使用synchronize关键字来进行,例如:

public class Counter {

    private volatile int count = 0;

    public synchronized int getAndIncr() {
        return this.count ++;
    }

}

这里可以看到,对于字段的简单设置和获取,volatile可以应付,但是我们想每次获取后自增加1,这样的操作就只能交给synchronize来做,这样做虽然可以但是性能较低,所以我们这里介绍一种新的无锁操作CAS。

什么是CAS(Compare And Swap)

  比较并且交换内存地址的数据,由CPU在指令级别上进行操作的原子性。

CAS包含三个参数:1、变量所在的地址A   2、变量应该的值V1   3、我们需要改的值V2。

如果说变量地址A上的值为V1,就用V2进行复制;如果值不是V1,则什么都不操作,返回V1的值。

自旋操作:在一个死循环里不停的进行CAS的操作,直到成功为止。

CAS实现原子操作的三大问题

  1. 当线程想把地址A的值V1改为V3时,当线程取到V1的值后再进行比较时,地址A的值从V1->V2->V1进行了改变,虽然V3可以正常赋值,但是比较的V1值已经不是取出来的V1了。
  2. 循环时间很长的话,CPU的负荷较大。
  3. 对一个变量进行操作可以,同时操作多个共享的变量有些麻烦。

CAS线程安全

  通过硬件层面的阻塞实现原子操作的安全性。

常用的原子操作类

  • 更新基本类型类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
  • 更新数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
  • 更新引用类:AtomicReference,AtomicReferenceFieldUpdater,AtomicMarkableReference
  • 原子更新字段类:AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicStampedReference
public class AtomicArray {

    static int[] value = new int[]{1, 2};
    static AtomicIntegerArray aia = new AtomicIntegerArray(value);

    public static void main(String[] args) {
        aia.set(0, 3);
        System.out.println(aia.get(0));
        System.out.println(value[0]);
    }
}

输出:

3
1

可以看出AtomicIntegerArray对象将int数组复制了一份,他的改变并没有影响到原有数组。

public class AtomicRef {

    static class User {
        private String name;
        private int age;

        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }

    static AtomicReference<User> userAtomicRef = new AtomicReference<AtomicRef.User>();

    public static void main(String[] args) {
        User user = new User("Mark", 25);
        userAtomicRef.set(user);
        User updUser = new User("Mark", 28);
        userAtomicRef.compareAndSet(user, updUser);
        System.out.println(userAtomicRef.get().getName());
        System.out.println(userAtomicRef.get().getAge());
    }

}

输出:

Mark
28

这里会首先进行比较,然后更新不一样的字段。

这里对CAS三大问题的第一个问题进行解决:

AtomicMarkableReference这个类会在数据被修改之后,标记位会由true变为false,判断这个标记位就可得知。

AtomicStampedReference这个类会在数据上添加时间戳,如果有数据修改时间戳会变掉,从而判断数据是否被修改。



本文索引关键字:

CAS:http://www.cnblogs.com/huanStephen/p/8213678.html#CAS

欢迎大家索引!

原文地址:https://www.cnblogs.com/huanStephen/p/8213678.html

时间: 2024-10-09 05:33:07

无锁机制下的原子性操作的相关文章

面试必备:CAS无锁机制

CAS无锁机制原理,面试高频问题之一,其实,日常开发中并不会直接使用CAS无锁机制,都是通过一系列封装好的工具类来使用, 说不定面试官不提问,都不知道有这么个东西存在. 1.能说一下你对CAS的理解吗? 参考回答: 通常我们提到保证多线程安全,会想到三种方式,一是使用Synchronize关键字,但是有个问题就是,使用了Synchronize加锁后的多线程相当于串行,执行效率并不是太高,所以在高并发场景下,使用第二种方式Lock锁,Lock锁要比使用Synchronize关键字在性能上有极大的提

无锁机制实现并发访问

对于并发控制而言, 锁是一种悲观的策略.它总是假设每一次的临界区操作会产生冲突,因此,必须对每次操作都小心翼翼.如果有多个线程同时需要访问临界区资源,就宁可牺牲性能让线程进行等待,所以说锁会阻塞线程执行. 而无锁是一种乐观的策略,它会假设对资源的访问是没有冲突的.既然没有冲突,自然不需要等待,所以所有的线程都可以在不停顿的状态下持续执行.那遇到冲突怎么办呢?无锁的策略使用一种叫做比较交换的技术(CAS Compare And Swap)来鉴别线程冲突,一旦检测到冲突产生,就重试当前操作直到没有冲

无锁的同步策略——CAS操作详解

1. 从乐观锁和悲观锁谈起 乐观锁和悲观锁是两种不同的解决并发问题的策略.悲观锁策略假定任何一次并发都会发生冲突,所以总是采用最严格的方式来进行并发控制.java中的独占锁(synchronized和重入锁)就是典型悲观锁实现,它只允许线程互斥的访问临界区,也就是阻塞式的同步方式.而乐观锁策略假定大部分情况下并发冲突不会发生,采用的是一种更为宽松的方式来进行并发控制.比如我们马上就要讲的CAS操作.它允许多线程非阻塞式地对共享资源进行修改,但同一时刻只有一个线程能够成功,其他线程被告知失败但并不

多线程读取全局变量 (在无锁状态下 会造成多少种值的出现)

int global = 0; // thread 1 for(int i = 0; i < 10; ++i) global -= 1; // thread 2 for(int i = 0; i < 10; ++i) global += 1; 之后global的可能的值是多少(多种可能)? 这个问题考虑的是全局变量global的加减操作不是原子操作,在加减过程中有可能被打断,从而产生的结果与预期不一样.上述global加减操作的汇编如下 ;windows下加操作 mov eax,dword p

使用无锁完成多线程模拟售票, 理解无锁是啥?

实现的模拟多线程实现售票是每个学习多线程的初学者必须要学会掌握的知识点, 既然掌握的它, 我们自然要举一反三 So~, 无锁版出现了 What无锁? 假如两个线程同时修改一个变量的场景下 我们需要三个值, 预期值(线程副本变量中的值), 主存值(从主存变量中的值), 新值(我们要设置的值) 如果 预期值 不等于 主存值 则忽略 新值 写入  =========> 这句话是一个原子操作, 是不可分割的(就是内存屏障), 在执行这个过程中, 是不会失去时间片的 如果 预期值 等于 主存值 则  新值

锁、CAS操作和无锁队列的实现

https://blog.csdn.net/yishizuofei/article/details/78353722 锁的机制 锁和人很像,有的人乐观,总会想到好的一方面,所以只要越努力,就会越幸运:有的人悲观,总会想到不好的一方面,患得患失,所以经常会做不好事.我一直把前一个当作为我前进的动力和方向,快乐充实的过好每一天. 常用的锁机制也有两种: 1.乐观锁:假设不会发生并发冲突,每次不加锁而去完成某项操作,只在提交操作时,检查是否违反数据完整性.如果因为冲突失败就继续重试,直到成功为止.而乐

透过 Linux 内核看无锁编程

非阻塞型同步 (Non-blocking Synchronization) 简介 如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步.同步可分为阻塞型同步(Blocking Synchronization)和非阻塞型同步( Non-blocking Synchronization). 阻塞型同步是指当一个线程到达临界区时,因另外一个线程已经持有访问该共享数据的锁,从而不能获取锁资源而阻塞,直到另外一个线程释放锁.常见的同步原语有 mutex.semaphore 等.如

Java锁机制(二)

CAS无锁机制 CAS:Compare and Swap,即比较再交换. Java内存模型:JMM(Java Memory Model) 在内存模型当中定义了一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一块工作内存,工作内存当中主内存数据的副本 当更新数据时,会将工作内存中的数据同步到主内存当中 CAS无锁机制:本身无锁,采用乐观锁的思想,在数据操作时对比数据是否一致,如果一致代表之前没有线程操作该数据,那么就会更新数据,如果不一致代表有县城更新则

Hibernate锁机制

业务逻辑的实现过程中,往往需要保证数据访问的排他性.因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的“锁”,即给我们选定的目标数据上锁,使其无法被其它程序修改. Hibernate 支持两种锁机制: 1. 悲观锁(Pessimistic Locking): 2. 乐观锁(Optimistic Locking): 1.悲观锁 它指的是对数据被外界修改持保守态度.假定任何时刻存取数据时,都可能有另一个客户也正在存取同一笔数据,为了保持数据被操