JDK Concurrent包组件概解 (1)- 并发组件底层原理sun.misc.Unsafe

目录

LockSupport

volidate

CAS


  • 并行(Parallel):  多个Cpu(核)同时刻分别执行各自的一个进程(线程),互相不抢占CPU资源。
  • 并发(Concurrent):单个Cpu(核)按时间片调度执行多个进程(线程)。

围绕并发访问共享变量有三种特性: 原子性、可见性、有序性

在并发包  java.util.concurrent内各组件的实现上总结来说用到了以下两种方式:

  1. Unsafe包提供支持Volatile效果的读写 及 CAS 操作方法。 通常是:一个直到执行方法unsafe.compareAndSwapXXX 成功时才break的循环(while)结构体。eg. #getAndAddXXX 方法
  2. volatile 关键字修饰成员变量 , 或 每次都通过Unsafe#getXXXVolatile方法拿到最新主存的值。

LockSupport

并发包里底层实现很多都使用 LockSupport来控制线程的挂起和再次执行。unpark函数可以先于park调用,unpark提供可执行的许可(不可累加,一次性),park 时若存在许可则马上继续执行

底层实现也依赖于sun.misc.Unsafe。 p.s:  反射Field#set的底层也用到了native Unsafe#putXXX 方法!

// 挂起线程
public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(false, 0L);
    setBlocker(t, null);
}
// 带挂起时长
public static void parkNanos(Object blocker, long nanos) {
    if (nanos > 0) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, nanos);
        setBlocker(t, null);
    }
}
// 唤醒
public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}

底层实现原理:

在Linux系统下,是用的Posix线程库pthread中的mutex(互斥量),condition(条件变量)来实现的; 维护了一个_counter的变量,简单说:当park时,这个变量被设置为0;当unpark时,这个变量被设置为1。

扩展: 

 1、一定是:在执行到某些(不限于  Object#wait、Thread#join、Thread#sleep 等)阻塞方法时,调用该线程的Interrupt()方法(thread1#interrupt();)阻塞状态下的线程将抛出一个InterruptedException中断异常被try-catch捕获处理,从而可以提早地终结被阻塞状态。 如果线程没有被阻塞,这时调用Interrupt()将不起作用,直到执行到阻塞方法才马上会抛出InterruptedException,捕获到InterruptedException后该线程中断标记就被清除

2、Thread#join()的实现是 :  由子线程实例调用,循环判定它自己线程状态是活跃中,则wait(0)。

volidate

在多线程并发访问同一个共享缓存变量时,该关键字保证了:

  1. 可见性 (线程本地缓存的更新能写入到主存,同时也能被其他线程感知并失效它们本地缓存)
  2. 有序性 (禁止指令重排序, 遵守happens-before规则)。

下面这段话摘自《深入理解Java虚拟机》:

  “观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”

lock前缀指令实际上相当于一个全内存屏障(不同的CPU架构上内存屏障的实现不一样),内存屏障会提供3个功能:

   1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

   2)它会强制将对缓存的修改操作立即写入主存;

   3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

CAS

sun.misc.Unsafe 提供了能够直接操作内存地址的native接口, 用来支持原子性。

通过自旋循环调用CAS, 汇编指令是:

  1. CPU多核: lock cmpxchg 指令。 单个   cmpxchg 指令并不是保证原子性, 是 lock 进行加锁保证 ,优先 “锁缓存行”,最次才是“锁总线”。
  2. CPU单核: 使用   cmpxchg 指令。 因为是单核,所以没有并发问题。
  1. Unsafe#objectFieldOffset("类名".class.getDeclaredField("成员变量名") ) : 获得成员变量相对于类在内存地址上的 fieldOffset 。
  2. unsafe#arrayBaseOffset("数组类型".class) : 用来确定每个数组内元素的内存偏移量。
  3. unsafe#putXXXVolatile、unsafe#getXXXVolatile  (XXX表示操作变量的类型) : 在获取、更新变量数据值时,像volidate关键字一样支持可见性、有序性。
  4. unsafe#compareAndSwapXXX (XXX表示操作变量的类型): CAS 更新某个实例对象 某个成员变量偏移量 的数值。

示例代码AtomicInteger的实现如下:

public class AtomicInteger extends Number implements java.io.Serializable {

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset; // "value" 相对于本类实例的内存偏移量

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

    private volatile int value;  //  volatile 修饰 保证 可见性和有序性

     public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

   // -- unsafe --
   public final int getAndAddInt(Object o, long offset, int delta) {
      int v;
      do {
       // while CAS 判定更新成功后, volatile方式获取最新的数据值
         v = this.getIntVolatile(o, offset); 
      } while(!this.compareAndSwapInt(o, offset, v, v + delta));
      return v;
   }

ConcurrentHashMap的实现里有数组偏移量的定义方式: 

// ConcurrentHashMap
   static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }

    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }

    // Unsafe mechanics
    private static final sun.misc.Unsafe U;
    private static final long SIZECTL;
    private static final long TRANSFERINDEX;
    private static final long BASECOUNT;
    private static final long CELLSBUSY;
    private static final long CELLVALUE;
    private static final long ABASE;
    private static final int ASHIFT;

    static {
        try {
            U = sun.misc.Unsafe.getUnsafe();
            Class<?> k = ConcurrentHashMap.class;
            SIZECTL = U.objectFieldOffset
                (k.getDeclaredField("sizeCtl"));
            TRANSFERINDEX = U.objectFieldOffset
                (k.getDeclaredField("transferIndex"));
            BASECOUNT = U.objectFieldOffset
                (k.getDeclaredField("baseCount"));
            CELLSBUSY = U.objectFieldOffset
                (k.getDeclaredField("cellsBusy"));
            Class<?> ck = CounterCell.class;
            CELLVALUE = U.objectFieldOffset
                (ck.getDeclaredField("value"));
            Class<?> ak = Node[].class;
 // map里<key,vlaue>封装成的Node[]数组相较于整个类实例对象的内存offset
            ABASE = U.arrayBaseOffset(ak); 
            int scale = U.arrayIndexScale(ak); 
            if ((scale & (scale - 1)) != 0)
                throw new Error("data type scale not a power of two");
 // ((long)index << ASHIFT) + ABASE  Node[]数组各元素相较于整个类实例对象的内存offset
            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
        } catch (Exception e) {
            throw new Error(e);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值