美文网首页
Java-多线程-Atomic&Unsafe魔术类

Java-多线程-Atomic&Unsafe魔术类

作者: 蓝色_笔记本 | 来源:发表于2021-06-29 10:23 被阅读0次

一、原子操作

Java中可以通过锁和循环CAS的方式来实现原子操作。JVM中的CAS操作正是利用了上文中提到的处理器提供的CMPXCHG指令实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止,具体的类可以参见juc下的atomic包内的原子类。
原子操作是针对CPU来说的,是一个不可能再分割的一个操作,要么不执行,要么执行完毕。CAS的锁就是操作系统的缓存行锁或者总线锁

多个CPU对主内存同一块缓存行进行CAS操作时,会遵循操作系统的缓存行一致性协议MESI,基于处理器提供的CMPXCHG指令,CPU会把变量读取到CPU寄存器中进行比较与交换操作(也就是CAS操作),优先操作完的CPU会把缓存行锁住并修改缓存行为修改M状态,其它CPU变为无效I。操作失败的CPU会等待操作成功的CPU把值写回主存内,这也是为什么CAS要通过循环来实现原子操作

注意:一个原子操作在Java中是通过CAS操作实现,那么一系列的原子操作是怎么实现的?通过同步块加锁就可以实现。

二、Atomic

在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。

  • 基本类:AtomicInteger、AtomicLong、AtomicBoolean;
  • 引用类型:AtomicReference、AtomicReference的ABA实例、AtomicStampedRerence、AtomicMarkableReference;
  • 数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray;
  • 属性原子修改器:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater

应用

创建相应的操作类型,传入初始值。

  public static void main(String[] args) throws InterruptedException {
    AtomicInteger atomicInteger = new AtomicInteger(1);
    for(int i = 0; i<100; i++)
    {
        new Thread(new Runnable() {
            @Override
            public void run() {
                atomicInteger.incrementAndGet();
            }
        }).start();
    }

    Thread.sleep(1000);
    System.out.println("原子操作结果---->"+atomicInteger.get());
  }

原子操作ABA问题

多线程同时操作同一个AtomicInteger时,比如T1 T2同时操作AtomicInteger atomic时,atomic初始值为2,T1 先读取atomic,值为2,这是T2进来,多次修改了atomic,先 2-->1,再 1-->2,T2结束,T1继续往下,把atomic修改了,而且修改成功了。这就是ABA问题,问题主要出现在T2先执行,期间T1修改了atomic而T2却不知道。

JUC包中AtomicReference可以解决AtomicInteger ABA的问题,原理就是引进了版本这一概念,atomic每修改一次,版本就变一次。这样,T1修改了atomic后T2能感知的到。

private static AtomicStampedReference<Integer> atomicStampedRef =
        new AtomicStampedReference<>(1, 0);
public static void main(String[] args){
    Thread main = new Thread(() -> {
        int stamp = atomicStampedRef.getStamp(); //获取当前标识别
        System.out.println("操作线程" + Thread.currentThread()+ "stamp="+stamp + ",初始值 a = " + atomicStampedRef.getReference());
        try {
            Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1);  //此时expectedReference未发生改变,但是stamp已经被修改了,所以CAS失败
        System.out.println("操作线程" + Thread.currentThread() + "stamp="+stamp + ",CAS操作结果: " + isCASSuccess);
    },"主操作线程");

    Thread other = new Thread(() -> {
        int stamp = atomicStampedRef.getStamp();
        atomicStampedRef.compareAndSet(1,2,stamp,stamp+1);
        System.out.println("操作线程" + Thread.currentThread() + "stamp="+atomicStampedRef.getStamp() +",【increment】 ,值 = "+ atomicStampedRef.getReference());
        stamp = atomicStampedRef.getStamp();
        atomicStampedRef.compareAndSet(2,1,stamp,stamp+1);
        System.out.println("操作线程" + Thread.currentThread() + "stamp="+atomicStampedRef.getStamp() +",【decrement】 ,值 = "+ atomicStampedRef.getReference());
    },"干扰线程");

    main.start();
    other.start();
}

三、Unsafe魔术类

Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。

image.png

Unsafe类为一单例实现,提供静态方法getUnsafe获取Unsafe实例,当且仅当调用getUnsafe方法的类为引导类加载器所加载时才合法,否则抛出SecurityException异常。

如何获取Unsafe实例?

1、从getUnsafe方法的使用限制条件出发,通过Java命令行命令-Xbootclasspath/a把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被引导类加载器加载,从而通过Unsafe.getUnsafe方法安全的获取Unsafe实例。

2、通过反射获取单例对象theUnsafe。

import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class UnsafeInstance {
    public static Unsafe reflectGetUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
Unsafe对象中使用CAS
public class AtomicStudentAgeUpdater {
    private String name ;
    private volatile int age;

    private static final Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset(AtomicStudentAgeUpdater.class.getDeclaredField("age"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    public void compareAndSwapAge(int old,int target){
        unsafe.compareAndSwapInt(this,valueOffset,old,target);
    }

    public AtomicStudentAgeUpdater(String name,int age){
        this.name = name;
        this.age = age;
    }

    public int getAge(){
        return this.age;
    }

    public static void main(String[] args) {
        AtomicStudentAgeUpdater updater = new AtomicStudentAgeUpdater("张三",18);
        updater.compareAndSwapAge(18,17);
    }
}
Unsafe线程调度
 static Object object = new Object();
 Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();
 //加锁解锁
 unsafe.monitorEnter(object);
 //同步块
 unsafe.monitorExit(object);
 //线程阻塞、唤醒
 unsafe.park();
 unsafe.unpark();
Unsafe内存屏障,防止指令重排
UnsafeInstance.reflectGetUnsafe().loadFence();//读屏障
UnsafeInstance.reflectGetUnsafe().storeFence();//写屏障
UnsafeInstance.reflectGetUnsafe().fullFence();//读写屏障

相关文章

  • Java-多线程-Atomic&Unsafe魔术类

    一、原子操作 Java中可以通过锁和循环CAS的方式来实现原子操作。JVM中的CAS操作正是利用了上文中提到的处理...

  • Hello Java

    目录 Java-基础(1/6) Java-对象(2/6) Java-核心库类 上(3/6) Java-核心库类下(...

  • Java-浅析Object类

    Java-浅析Object类 ++2016.7.19++byside @Java-浅析Object类 ======...

  • java-多线程

    介绍一下Syncronized锁。如果用这个关键字修饰一个静态方法,锁住了什么?如果修饰成员方法,锁住了什么? 修...

  • java-多线程

    多线程 synchronized 1、synchronized关键字 简介解决多个线程之间访问资源的同步性。保证被...

  • java-类

    User.java UserTest.java

  • Java- 继承和多态

    Java- 继承和多态 可以从现有的类派生出新类。这称为类的继承。新类称为次类、子类或派生类。现有的类称为超类、父...

  • 多线程

    创建一个多线程 创建多线程-继承线程类 创建多线程-实现Runnable接口 创建多线程-匿名类code

  • 零基础小白Python入门必看:面向对象之典型魔术方法

    魔术方法 查看类的魔术方法 class A: pass dir(A) # 可以得到类所有公有成员 复制代码 输...

  • 中国魔术剧目开创者—魔术师吴曌

    魔术师吴曌,哈利波特魔术剧主演,从事魔术10年,擅长近景魔术、舞台魔术以及大型幻术类魔术。优雅的绅士风格表演使他在...

网友评论

      本文标题:Java-多线程-Atomic&Unsafe魔术类

      本文链接:https://www.haomeiwen.com/subject/ebpcultx.html