java并发原理实战(7) -- 原子类使用和原理理解

简介: java并发原理实战(7) -- 原子类使用和原理理解

使用


1dc618a0ed9580ce8bfa6facb208c08f.png

原子更新基本类型


原子更新数组


原子更新抽象类型


原子更新字段


public class Sequence {
    private int value;
    public int getNext() {
        return value++;
    }
    public static void main(String[] args) {
        Sequence sequence = new Sequence();
//        while (true){
//            System.out.println(sequence.getNext());
//        }
        new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + " " + sequence.getNext());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();
        new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + " " + sequence.getNext());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();
        new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + " " + sequence.getNext());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();
    }
}


运行结果:

5d4c6812c8535adbb050f4ddf2e1bce8.png

会看到:多个线程对value++,出现了 value一样的情况,即多线程安全问题。


用原子类解决下:


public class Sequence {
    private AtomicInteger value = new AtomicInteger(0);
    public int getNext() {
        return value.getAndIncrement();
    }
    public static void main(String[] args) {
        Sequence sequence = new Sequence();
        new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + " " + sequence.getNext());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();
        new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + " " + sequence.getNext());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();
        new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + " " + sequence.getNext());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();
    }
}


运行结果:


46a9d80a6e05e4e3b19d57a0ee70bcdf.png

发现没有安全性问题。


原子类AtomicInteger原理


在Java中,有很多方法可以保证多线程下数据的安全,AtomicXXXX这些类就是其中的一种,原子类,可以保证每一步操作都是原子操作。这次就对AtomicInteger的源码进行学习。


首先看一下这个类的类变量和成员变量:


//类变量 unsafe类【java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作】
//这里的这个变量就是用来进行cpu级别的原子操作。
private static final Unsafe unsafe = Unsafe.getUnsafe();
//这个变量实际上是由下面的static块来赋值的,可以由赋值看出来,这个值是下面的value属性在每个AtomikInteger对象中的位置偏移量,用这个值既可以找到在具体每个对象的内存中的内存地址。
private static final long valueOffset;
static {
    try {
       valueOffset = unsafe.objectFieldOffset
           (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}
//volatile修饰,AtomicInteger的值,在直接内存中,多个线程下可以直接获取值。
private volatile int value;


看完了这个类的内部的变量,其实大概可以猜到这个类怎么完成的原子操作了,使用volatile修饰的value来存储值,保证每个线程都可以随时读到值,然后每一步操作都使用CAS(compare and swap)这样即可以保证一直能原子写入,下面来看看源码到底是不是这样。


来看一下incrementAndGet这个方法,实际上就是++i的操作,但是保证原子性。


public final int incrementAndGet() {
    //用get出来的值+1,前面的方法是unsafe中实现的 i++。对value属性进行操作。
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//Unsafe中的。这个方法可以看到一直在做do-while,直到CAS成功(获取AtomicInteger对象上的value属性,然后CAS检查保证值是var5的时候将他变成var5+1)。
//其中getIntVolatile和compareAndSwapInt 都是native方法,用C写的。CAS底层貌似是使用了cpu的cpxchg(compare*change)。
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
   do {
       var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}

总结


所有其他的方法都大同小异,使用volatile修饰的value来存储值,保证每个线程都可以随时读到值,然后每一步操作都使用CAS(compare and swap)这样即可以保证原子写入。



相关文章
|
16天前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
27 3
|
16天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
46 2
|
20天前
|
Java
Java基础却常被忽略:全面讲解this的实战技巧!
本次分享来自于一道Java基础的面试试题,对this的各种妙用进行了深度讲解,并分析了一些关于this的常见面试陷阱,主要包括以下几方面内容: 1.什么是this 2.this的场景化使用案例 3.关于this的误区 4.总结与练习
|
2月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
2月前
|
Java
Java之CountDownLatch原理浅析
本文介绍了Java并发工具类`CountDownLatch`的使用方法、原理及其与`Thread.join()`的区别。`CountDownLatch`通过构造函数接收一个整数参数作为计数器,调用`countDown`方法减少计数,`await`方法会阻塞当前线程,直到计数为零。文章还详细解析了其内部机制,包括初始化、`countDown`和`await`方法的工作原理,并给出了一个游戏加载场景的示例代码。
Java之CountDownLatch原理浅析
|
2月前
|
Java 索引 容器
Java ArrayList扩容的原理
Java 的 `ArrayList` 是基于数组实现的动态集合。初始时,`ArrayList` 底层创建一个空数组 `elementData`,并设置 `size` 为 0。当首次添加元素时,会调用 `grow` 方法将数组扩容至默认容量 10。之后每次添加元素时,如果当前数组已满,则会再次调用 `grow` 方法进行扩容。扩容规则为:首次扩容至 10,后续扩容至原数组长度的 1.5 倍或根据实际需求扩容。例如,当需要一次性添加 100 个元素时,会直接扩容至 110 而不是 15。
Java ArrayList扩容的原理
|
1月前
|
Java 程序员
Java基础却常被忽略:全面讲解this的实战技巧!
小米,29岁程序员,分享Java中`this`关键字的用法。`this`代表当前对象引用,用于区分成员变量与局部变量、构造方法间调用、支持链式调用及作为参数传递。文章还探讨了`this`在静态方法和匿名内部类中的使用误区,并提供了练习题。
36 1
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
70 6
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
2月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####