zoukankan      html  css  js  c++  java
  • java.util.concurrent各组件分析 一 sun.misc.Unsafe

    java.util.concurrent各组件分析 一 sun.misc.Unsafe

    说到concurrent包也叫并发包,该包下主要是线程操作,方便的进行并发编程,提到并发那么锁自然是不可缺少的,包中的类存在了大量关于锁的操作

    因此有必要先了解java中锁的原理,锁的底层就是sun.misc.Unsafe类,这个类可以说是java并发包的基础,基本上并发包的所有组件都是依赖Unsafe来进行底层同步操作的

    java不能直接访问操作系统底层,而是通过本地方法来访问,Unsafe类提供了硬件级别的原子操作,在100+的方法中大部分都是native类型的,可以进行底层操作,
    比如操作内存、低级同步、CAS方法、操作Class、操作Object等等。实际通过该类就像C、C++一样可以精确操作内存。

    但java特点就是面向对象,表面上是不进行类似指针形式的直接内存操作,因此该类是不提倡我们在上层的代码中直接使用的。

    1.简单知道可以操作内存
    类中有3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法是对应的。
    再深入就追踪到C的类库中了,不向下看了。

    2.与并发编程相关的几类方法
    2.1 CAS方法
    2.2 通过getBooleanVolatile等方法,定位字段,进行存取
    2.2 操作线程的方法,park线程等待,unpark唤醒线程

    2.3 CAS方法,主要是compareAndSwapXXX方法
    compareAndSwapObject提供了对一个对象引用进行CAS的能力
    compareAndSwapInt提供了对一个32位整数进行CAS操作的能力
    compareAndSwapLong提供了对64位整数进行CAS操作的能力

    	/**
    	* 比较obj的offset处内存位置中的值和期望的值,如果相同则更新。此更新是不可中断的。
    	* 
    	* @param obj 需要更新的对象
    	* @param offset obj中整型field的偏移量
    	* @param expect 希望field中存在的值
    	* @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值
    	* @return 如果field的值被更改返回true
    	*/
    	public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);
    

     

    CAS操作看起来是很爽的,但是CAS操作会存在ABA问题,简单来说就是某线程进行compareAndSwap,在进行比较时,另一个线程对该内存值进行了两次或以上的更新操作,
    但最终还原了值,这时对前一个线程来说相当于没变CAS操作成功。但是这个成功不代表没问题,如果值是对象、链表等其中的属性或某种状态是发生了变化的就有问题了。
    解决ABA问题可以使用AtomicStampedReference/AtomicMarkableReference类进行,其带有数据版本号完全避免了ABA问题

    2.2 通过getBooleanVolatile等方法,定位字段,进行存取

    1.获取内存位置有staticFieldOffset、objectFieldOffset方法实现,方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的。

    2.还可以通过下面的一系列,获取内存地址的方法

    public native void putObjectVolatile(Object var1, long var2, Object var4);
    
        public native int getIntVolatile(Object var1, long var2);
    
        public native void putIntVolatile(Object var1, long var2, int var4);
    
        public native boolean getBooleanVolatile(Object var1, long var2);
    
        public native void putBooleanVolatile(Object var1, long var2, boolean var4);
    
        public native byte getByteVolatile(Object var1, long var2);
    
        public native void putByteVolatile(Object var1, long var2, byte var4);
    
        public native short getShortVolatile(Object var1, long var2);
    
        public native void putShortVolatile(Object var1, long var2, short var4);
    
        public native char getCharVolatile(Object var1, long var2);
    
        public native void putCharVolatile(Object var1, long var2, char var4);
    
        public native long getLongVolatile(Object var1, long var2);
    
        public native void putLongVolatile(Object var1, long var2, long var4);
    
        public native float getFloatVolatile(Object var1, long var2);
    
        public native void putFloatVolatile(Object var1, long var2, float var4);
    
        public native double getDoubleVolatile(Object var1, long var2);
    
        public native void putDoubleVolatile(Object var1, long var2, double var4);
    
        public native void putOrderedObject(Object var1, long var2, Object var4);
    
        public native void putOrderedInt(Object var1, long var2, int var4);
    
        public native void putOrderedLong(Object var1, long var2, long var4);

    3.数组类型有BASE_OFFSET结尾的这些常量辅助去找位置
    public static final int ARRAY_BOOLEAN_BASE_OFFSET;
    public static final int ARRAY_BYTE_BASE_OFFSET;
    public static final int ARRAY_SHORT_BASE_OFFSET;
    public static final int ARRAY_CHAR_BASE_OFFSET;

    2.3 操作线程的方法,park线程等待,unpark唤醒线程
    park 让线程进入等待,并释放锁。调用后线程将一直阻塞知道超时或中断等条件出现。
    unpark 唤醒等待的线程使其恢复正常。

    在使用Unsafe时,我们需要Unsafe对象的实例。但Unsafe unsafe = new Unsafe()是不行的,其构造方法是私有的,它也有一个静态的getUnsafe()方法,
    但如果直接调用Unsafe.getUnsafe(),会报SecurityException异常,只能从受信任的代码中使用这个方法。
    这个受信任就比较麻烦,但Unsafe类包含一个私有的、名为theUnsafe的实例,可以通过Java反射等窃取该变量。
    说这么多我们就会发现Unsafe在刻意隐藏,也就前面说的不推荐上传应用使用该类。

    并发包内提供了LockSupport封装了Unsafe对象的类,一般我们都用这个封装类。

    public class LockSupport {
            private LockSupport() {} // Cannot be instantiated.
            private static final sun.misc.Unsafe UNSAFE;
            static {
                try {
                    UNSAFE = sun.misc.Unsafe.getUnsafe();
                    Class<?> tk = Thread.class;
                    parkBlockerOffset = UNSAFE.objectFieldOffset
                        (tk.getDeclaredField("parkBlocker"));
                } catch (Exception ex) { throw new Error(ex); }
            }
            
            private static void setBlocker(Thread t, Object arg) {
                // Even though volatile, hotspot doesn't need a write barrier here.
                UNSAFE.putObject(t, parkBlockerOffset, arg);
            }
            
            public static void park() {
                UNSAFE.park(false, 0L);
            }
            
            public static void park(Object blocker) {
                Thread t = Thread.currentThread();
                setBlocker(t, blocker);
                UNSAFE.park(false, 0L);
                setBlocker(t, null);
            }
            
            public static void unpark(Thread thread) {
                if (thread != null)
                    UNSAFE.unpark(thread);
            }
        }
        
        public class Thread{  
            volatile Object parkBlocker;  
        }
        主要包含的点:
        1.利用Unsafe直接操作内存来设置blocker
        parkBlocker是thread中的volatile类型的变量。UNSAFE.objectFieldOffset可以获得这里是blocker的内存偏移offset,即物理内存位置,和c++就一模一样了。
        setBlocker方法是把arg对象设置到Thread的parkBlocker属性上。
        parkBlocker是用来表明本线程是在哪个对象上阻塞
        
        2.park有两个方法,带参数的就是指明锁对象,即表明该线程锁在那个对象上。不带参数就不表明了。
        
        3.unpark直接唤醒
  • 相关阅读:
    Shiro笔记(三)shiroFilter拦截器配置原则
    Shiro笔记(二)Shiro集成SpringMVC的环境配置
    Shiro笔记(一)Shiro整体介绍
    javaNIO的总结
    Redis的工作流程
    Nginx的配置安装和使用
    Linux下java开发环境配置总结
    php 基础知识 post 和get 两种传输方式的区别
    php 高级 多台web服务器共享session的方法
    php 基础知识 SESSION 和 COOKIE 的区别
  • 原文地址:https://www.cnblogs.com/zhaojj/p/8024511.html
Copyright © 2011-2022 走看看