java多线程 -- 原子量 变量 CAS

简介: 多线程原子性问题的产生和解决原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。1. volatile 保证内存可见性,可以查看atomic中变量是使用volatile来进行修饰的: public class AtomicInteger extends Number implements java.

多线程原子性问题的产生和解决

原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。

1. volatile 保证内存可见性,可以查看atomic中变量是使用volatile来进行修饰的:

 

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

2. CAS(Compare-And-Swap)比较并交换,算法保证数据变量的原子性
 CAS 算法是硬件对于并发操作的支持
 CAS 包含了三个操作数:
 ①内存值 V
 ②预估值 A
 ③更新值 B
 当且仅当 V == A 时, V = B; 否则,不会执行任何操作。

模拟CAS算法:

/*
 * 模拟 CAS 算法
 */
public class TestCompareAndSwap {

    public static void main(String[] args) {
        final CompareAndSwap cas = new CompareAndSwap();
        
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    int expectedValue = cas.get();
                    boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
                    System.out.println(b);
                }
            }).start();
        }
        
    }
    
}

class CompareAndSwap{
    private int value;
    
    //获取内存值
    public synchronized int get(){
        return value;
    }
    
    //比较
    public synchronized int compareAndSwap(int expectedValue, int newValue){
        int oldValue = value;
        
        if(oldValue == expectedValue){
            this.value = newValue;
        }
        
        return oldValue;
    }
    
    //设置
    public synchronized boolean compareAndSet(int expectedValue, int newValue){
        return expectedValue == compareAndSwap(expectedValue, newValue);
    }
}

 

其他博文关于CAS的详细描述:http://blog.csdn.net/ls5718/article/details/52563959

  1. AtomicBoolean 可以用原子方式更新的 boolean 值。
  2. AtomicInteger 可以用原子方式更新的 int 值。
  3. AtomicIntegerArray 可以用原子方式更新其元素的 int 数组。
  4. AtomicIntegerFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。
  5. AtomicLong 可以用原子方式更新的 long 值。
  6. AtomicLongArray 可以用原子方式更新其元素的 long 数组。
  7. AtomicLongFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。
  8. AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。
  9. AtomicReference<V> 可以用原子方式更新的对象引用。
  10. AtomicReferenceArray<E> 可以用原子方式更新其元素的对象引用数组。
  11. AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。
  12. AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。

先看下AtomicInteger:

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by soyoungboy on 2017/3/28.
 */
public class AtomicDemo {
    public static void main(String[] args) {
        testAtomicInteger();
    }

    private static void testAtomicInteger() {
        AtomicInteger atomicInteger = new AtomicInteger(1);
        //get()获取当前值。
        int x = atomicInteger.get();
        System.out.println("get()获取当前值 = " + x);
        //addAndGet(int delta)以原子方式将给定值与当前值相加
        int i = atomicInteger.addAndGet(2);
        System.out.println("addAndGet(int delta)以原子方式将给定值与当前值相加 = " + i);
        // decrementAndGet()以原子方式将当前值减 1。
        int i1 = atomicInteger.decrementAndGet();
        System.out.println("decrementAndGet()以原子方式将当前值减 1 = " + i1);
        //doubleValue() 以 double 形式返回指定的数值。
        double doubleValue = atomicInteger.doubleValue();
        System.out.println("doubleValue() 以 double 形式返回指定的数值。 = " + doubleValue);
        //floatValue()以 float 形式返回指定的数值。
        float floatValue = atomicInteger.floatValue();
        System.out.println("floatValue()以 float 形式返回指定的数值。。 = " + floatValue);
        // intValue() 以 int 形式返回指定的数值。
        int intValue = atomicInteger.intValue();
        System.out.println("intValue() 以 int 形式返回指定的数值。= " + intValue);
        //etAndSet(int newValue)以原子方式设置为给定值,并返回旧值。
        int andAdd = atomicInteger.getAndAdd(20);
        System.out.println("---------------------------------------------------------");
        System.out.println("getAndAdd(int delta)以原子方式将给定值与当前值相加。旧值 = " + andAdd);
        System.out.println("新值 = " + atomicInteger.get());
        System.out.println("---------------------------------------------------------");
        //getAndDecrement()以原子方式将当前值加 1。
        int andDecrement = atomicInteger.getAndDecrement();
        System.out.println("getAndDecrement()以原子方式将当前值减 1。 = " + andDecrement);
        //getAndDecrement()以原子方式将当前值减 1。
        int andIncrement = atomicInteger.getAndIncrement();
        System.out.println("getAndDecrement()以原子方式将当前值减 1。" + andIncrement);
        //以原子方式将当前值加 1。
        int incrementAndGet = atomicInteger.incrementAndGet();
        System.out.println("以原子方式将当前值加 1。" + incrementAndGet);

    }
}

 结果:

get()获取当前值 = 1
addAndGet(int delta)以原子方式将给定值与当前值相加 = 3
decrementAndGet()以原子方式将当前值减 1 = 2
doubleValue() 以 double 形式返回指定的数值。 = 2.0
floatValue()以 float 形式返回指定的数值。。 = 2.0
intValue() 以 int 形式返回指定的数值。= 2
---------------------------------------------------------
getAndAdd(int delta)以原子方式将给定值与当前值相加。旧值 = 2
新值 = 22
---------------------------------------------------------
getAndDecrement()以原子方式将当前值减 1。 = 22
getAndDecrement()以原子方式将当前值减 1。21
以原子方式将当前值加 1。23

 原子更新数组

  1. AtomicIntegerArray
  2. AtomicLongArray
  3. AtomicReferenceArray<E>
/**
 * Created by soyoungboy on 2017/3/28.
 */
public class AtomicDemo {
    public static void main(String[] args) {
        testAtomicInteger();
    }

    private static void testAtomicInteger() {
        Person person = new Person(11,"小强");
        Person person1 = new Person(11,"大强");
        Person[] peoples = new Person[]{person,person1};
        AtomicReferenceArray<Person> personAtomicReferenceArray = new AtomicReferenceArray<Person>(peoples);
        printArray(personAtomicReferenceArray);
        // 以原子方式将位置 i 的元素设置为给定值,并返回旧值。
        Person person2 = new Person(22, "老秦");
        Person andSet = personAtomicReferenceArray.getAndSet(1, person2);
        System.out.println("返回的旧值 = "+andSet);
        printArray(personAtomicReferenceArray);
        //weakCompareAndSet(int i, E expect, E update)
        // 如果当前值 == 预期值,则以原子方式将位置 i 的元素设置为给定的更新值。
        Person person3 = new Person(23, "哈哈");
        //因为上面替换为老秦的person,所以如果是老秦的person,就替换
        personAtomicReferenceArray.weakCompareAndSet(1,person2,person3);
        printArray(personAtomicReferenceArray);
    }


    private static void printArray(AtomicReferenceArray<Person> personAtomicReferenceArray) {
        System.out.println("-------------------------------------");
        for (int i = 0;i<personAtomicReferenceArray.length();i++){
            String s = personAtomicReferenceArray.get(i).toString();
            System.out.println(s);
        }
    }
}

结果:

-------------------------------------
Person{age=11, name='小强'}
Person{age=11, name='大强'}
返回的旧值 = Person{age=11, name='大强'}
-------------------------------------
Person{age=11, name='小强'}
Person{age=22, name='老秦'}
-------------------------------------
Person{age=11, name='小强'}
Person{age=23, name='哈哈'}

原子更新引用类型

  1. AtomicReference:原子更新引用类型。
  2. AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
  3. AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef,boolean initialMark)。

举例子:

import java.util.concurrent.atomic.AtomicReference;

/**
 * Created by Administrator on 2017/3/28.
 */
public class AtomicReferenceDemo {
    public static void main(String[] args) {
        testAtomicReference();
    }


    private static void testAtomicReference() {
        Person person1 = new Person(1,"姚明");
        Person person2 = new Person(2,"易建联");
        Person person3 = new Person(3,"王思聪");
        AtomicReference<Person> personAtomicReference = new AtomicReference<>(person1);
        System.out.println("personAtomicReference"+personAtomicReference.get().toString());
        System.out.println("------------------------------------------------------------");
        //compareAndSet(V expect, V update)如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
        //person2肯定不是期望值,所以不会设置为person3,因此值应该为person1
        personAtomicReference.compareAndSet(person2,person3);
        System.out.println("personAtomicReference"+personAtomicReference.get().toString());
        System.out.println("------------------------------------------------------------");
        // getAndSet(V newValue) 以原子方式设置为给定值,并返回旧值。
        Person andSet = personAtomicReference.getAndSet(person2);
        System.out.println("旧值 = "+andSet);
        System.out.println("新值 = "+personAtomicReference.get().toString());
        personAtomicReference.lazySet(person3);
        //lazySet(V newValue)最终设置为给定值。
        System.out.println("lazySet  "+personAtomicReference.get().toString());
    }
}

结果:

personAtomicReferencePerson{age=1, name='姚明'}
------------------------------------------------------------
personAtomicReferencePerson{age=1, name='姚明'}
------------------------------------------------------------
旧值 = Person{age=1, name='姚明'}
新值 = Person{age=2, name='易建联'}
lazySet  Person{age=3, name='王思聪'}

 原子更新字段类

  1. AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
  2. AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  3. AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。

newUpdater(Class<U> tclass, String fieldName) 
          使用给定字段为对象创建和返回一个更新器。

这是这个不同于上面类的方法,其他基本上一样。

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * Created by Administrator on 2017/3/28.
 */
public class AtomicIntegerFieldUpdaterDemo {
    public static void main(String[] args) {
        testAtomicReference();
    }


    private static void testAtomicReference() {
        Person person1 = new Person(1,"姚明");
        AtomicReferenceFieldUpdater<Person, String> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(
            Person.class, String.class, "name");
        // 如果当前值 == 预期值,则以原子方式将此更新器管理的给定对象的字段设置为给定的更新值。
        atomicReferenceFieldUpdater.compareAndSet(person1,person1.name,"哈哈");
        System.out.println("person1  = "+person1.toString());

        AtomicReferenceFieldUpdater<Person, Integer> atomicReferenceFieldUpdater1 = AtomicReferenceFieldUpdater.newUpdater(
            Person.class, Integer.class, "age");
        // 如果当前值 == 预期值,则以原子方式将此更新器管理的给定对象的字段设置为给定的更新值。
        atomicReferenceFieldUpdater1.compareAndSet(person1,person1.age,22);
        System.out.println("person1  = "+person1.toString());

    }
}

结果:

person1  = Person{age=1, name='哈哈'}
person1  = Person{age=22, name='哈哈'}

 

相关文章
|
9天前
|
算法 Java 开发者
Java中的多线程编程:概念、实现与性能优化
【4月更文挑战第9天】在Java编程中,多线程是一种强大的工具,它允许开发者创建并发执行的程序,提高系统的响应性和吞吐量。本文将深入探讨Java多线程的核心概念,包括线程的生命周期、线程同步机制以及线程池的使用。接着,我们将展示如何通过继承Thread类和实现Runnable接口来创建线程,并讨论各自的优缺点。此外,文章还将介绍高级主题,如死锁的预防、避免和检测,以及如何使用并发集合和原子变量来提高多线程程序的性能和安全性。最后,我们将提供一些实用的性能优化技巧,帮助开发者编写出更高效、更稳定的多线程应用程序。
|
8天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
1天前
|
安全 Java
java多线程(一)(火车售票)
java多线程(一)(火车售票)
|
1天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
1天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。
|
2天前
|
缓存 分布式计算 监控
Java并发编程:深入理解线程池
【4月更文挑战第17天】在Java并发编程中,线程池是一种非常重要的技术,它可以有效地管理和控制线程的执行,提高系统的性能和稳定性。本文将深入探讨Java线程池的工作原理,使用方法以及在实际开发中的应用场景,帮助读者更好地理解和使用Java线程池。
|
2天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
9 1
|
2天前
|
Java 开发者
Java中多线程并发控制的实现与优化
【4月更文挑战第17天】 在现代软件开发中,多线程编程已成为提升应用性能和响应能力的关键手段。特别是在Java语言中,由于其平台无关性和强大的运行时环境,多线程技术的应用尤为广泛。本文将深入探讨Java多线程的并发控制机制,包括基本的同步方法、死锁问题以及高级并发工具如java.util.concurrent包的使用。通过分析多线程环境下的竞态条件、资源争夺和线程协调问题,我们提出了一系列实现和优化策略,旨在帮助开发者构建更加健壮、高效的多线程应用。
2 0
|
3天前
|
缓存 监控 Java
Java并发编程:线程池与任务调度
【4月更文挑战第16天】Java并发编程中,线程池和任务调度是核心概念,能提升系统性能和响应速度。线程池通过重用线程减少创建销毁开销,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。任务调度允许立即或延迟执行任务,具有灵活性。最佳实践包括合理配置线程池大小、避免过度使用线程、及时关闭线程池和处理异常。掌握这些能有效管理并发任务,避免性能瓶颈。
|
3天前
|
设计模式 运维 安全
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第15天】在Java开发中,多线程编程是提升应用程序性能和响应能力的关键手段。然而,它伴随着诸多挑战,尤其是在保证线程安全的同时如何避免性能瓶颈。本文将探讨Java并发编程的核心概念,包括同步机制、锁优化、线程池使用以及并发集合等,旨在为开发者提供实用的线程安全策略和性能优化技巧。通过实例分析和最佳实践的分享,我们的目标是帮助读者构建既高效又可靠的多线程应用。