006JDK Unsafe类使用与CAS原子特性

由于java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作,主要提供了以下功能:

内存操作

字段的定位与修改

挂起与恢复

CAS操作(乐观锁) 

1.内存操作:

        类中提供的3个本地方法allocateMemoryreallocateMemoryfreeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应.

006JDK Unsafe类使用与CAS原子特性_第1张图片

2.字段的定位与修改:

        可以定位对象某字段的内存位置也可以修改对象的字段值,即使它是私有的.

3.挂起与恢复

将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现;unpark可以终止一个挂起的线程,使其恢复正常;整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。

4.CAS操作(乐观锁) 

首先介绍一下什么是Compare And Swap(CAS)?简单的说就是比较并交换

CAS操作包含三个操作数——内存位置(V预期原值(A)和新值(B)

        如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。

CAS 有效地说明了:

“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”

        Java并发包(java.util.concurrent)中大量使用了CAS操作,涉及到并发的地方都调用了sun.misc.Unsafe类方法进行CAS操作,在Unsafe中是通过compareAndSwapXXX方法实现的。方法如下:

006JDK Unsafe类使用与CAS原子特性_第2张图片

知识点:

1.Unsafe类java无法直接调用,只有操作系统底层才能使用;

代码演示:

import java.lang.reflect.Field;
import java.util.Arrays;

import sun.misc.Unsafe;

public class UseUnsafe {
    
    private static int byteArrayBaseOffset;
    
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        /*Unsafe类java无法直接调用,只有操作系统底层才能调用;
         * 通过反射强制调用java.lang.reflect.Field,获取Unsafe类(虽然无法直接调用,但是可以强制获取);
         * 需要自己import sun.misc.Unsafe进来,不自动提供;
         * NoSuchFieldException(反射异常), SecurityException(权限异常), IllegalArgumentException(非法参数异常), IllegalAccessException(非法许可异常)
         */
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        //设置许可为true,即获取许可,必要步骤
        theUnsafe.setAccessible(true);
        
        Unsafe UNSAFE = (Unsafe) theUnsafe.get(null);
        System.out.println(UNSAFE);
        
        byte[] data = new byte[10];
        System.out.println(Arrays.toString(data));
        
        /* 调用一个本地方法,获得data数组中第一个元素的内存偏移地址;
         * 即获得元素的实际物理偏移量;
         */
        byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class);
        System.out.println("byte[]数组的第一个元素的偏移地址"+byteArrayBaseOffset);
        
        //设置指定的字节数组,修改其内存字段的位置,byteArrayBaseOffset基础的偏移量
        //1.在data这个数组对象设置第一个位置为1
        UNSAFE.putByte(data, byteArrayBaseOffset,(byte)1);
        //2.在data这个数组对象设置第七个位置为8
        UNSAFE.putByte(data, byteArrayBaseOffset+6,(byte)8);
        
        System.out.println(Arrays.toString(data));
    
    }

你可能感兴趣的:(线程安全)