请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

4.1节关于compareAndSwapInt()方法的形参解释是不是有问题?

4.1节7分54秒左右,讲解CAS方法时

unsafe.compareAndSwapInt(var1, var2, var5, var5 + var4)

解释`var2`是原来的值,var5是底层的值,最后一个形参是更换的值。实现原理是var2与var5进行比较,如果相同则硬件把值置为var5+var4。

但我查看调用:

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

发现`var2`是valueOffset,即AtomicInteger中值的地址偏移量,这个值是不会变的。百度查阅资料后发现,正确的流程解释应该是:

`var2`是AtomicInteger的value的相对地址值,`var5`是期望值,`var5 + var4`是置换值,与compareAndSet类似,每次循环调本地方法时,传最新的预期值,和符合修改值。由本地方法中硬件层具体实现,如果预期值和最新值相同,将AtomicInteger对象的value值改为符合修改值

正在回答 回答被采纳积分+3

5回答

wwpbjing 2018-11-03 20:47:01

尝试班门弄斧地解答:
我最初对于Java语言的理解是:Java无法像C/C++那样使用指针操作内存,
其实Java语言偷偷地埋了个后门:sun.misc.Unsafe(Java9开始计划废弃),
这个类可以完成:
1、分配/重新分配/释放内存,(allocateMemory/reallocateMemory/freeMemory)
2、获得对象中某个属性/静态属性的在该对象中的偏移地址(objectFieldOffset/staticFieldOffset),
3、使用CAS等CPU的操作原语(compareAndSwapObject/compareAndSwapInt/compareAndSwapLong)
4、存取普通变量/volatile变量(putObject/getObject/putObjectVolatile/getObjectVolatile...)
5、线程挂起/恢复(park/unpark...)
6、内存屏障(loadFence/storeFence)
7、synchronized关键字中monitor对象锁定解锁(monitorEnter/monitorExit)
...
这些方法是Java很多底层功能的实现基石
1、NIO直接内存(Direct Memory,参见java.nio.DirectByteBuffer类)
2、大部分Unsafe类中的方法都需要一个类型为long的offset参数:
 eg:

  public native Object getObject(Object obj, long offset);
  public final native boolean compareAndSwapInt(obj, offset, expect, update);

3、可用于实现基于CAS的乐观锁等操作
4、存取变量/volatile变量
5、java.util.concurrent.locks.LockSupport类中park/unpark系列方法实现的基础,LockSupport又是J.U.C实现的基础,用以实现线程挂起/恢复
6&7、还需要结合JVM才能彻底明白,底层的东西,自己也半吊子,就省略了
再多插一句:
sun.misc.Unsafe类的静态工厂方法比较有意思:

public static Unsafe getUnsafe() {
 Class var0 = Reflection.getCallerClass();
 if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
  throw new SecurityException("Unsafe");
 } else {
  return theUnsafe;
 }
}

getUnsafe方法会验证类加载器,只有启动类加载器加载的类才可以调用Unsafe.getUnsafe方法,
自己写的类由于是应用(系统)类加载器加载的就会抛异常:SecurityException
此举显然是JDK开发者不希望应用开发者调用Unsafe类中的方法,毕竟这个类功能太过强大也太过底层,
使用不当是有很大风险的,当然我们可以使用Java黑魔法————反射拿到Unsafe实例
我提供两种反射思路拿到Unsafe实例:

// 获取Unsafe类中属性"theUnsafe"获取实例
try {
 Field field = Unsafe.class.getDeclaredField("theUnsafe");
 field.setAccessible(true);
 Unsafe unsafe = (Unsafe) field.get(null);
 System.out.println(unsafe);
} catch (NoSuchFieldException | IllegalAccessException e) {
 e.printStackTrace();
}
// 通过私有构造器创建实例
try {
 Constructor<Unsafe> c = Unsafe.class.getDeclaredConstructor();
 c.setAccessible(true);
 Unsafe unsafe = c.newInstance();
 System.out.println(unsafe);
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
 e.printStackTrace();
}

有了Unsafe实例,我们就可以玩了:

public class CASDemo {
    private static Unsafe unsafe;
    private int count = 0;
    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws Exception {
        CASDemo instance = new CASDemo();
        // 使用反射API获取count变量的Field
        Field countVariableField = CASDemo.class.getDeclaredField("count");
        //  使用Unsafe类中objectFieldOffset方法获取CASDemo类中count属性的偏移地址:offset
        long countVariableAddressOffset = unsafe.objectFieldOffset(countVariableField);
        System.out.println("CASDemo类中count属性内存偏移地址:" + countVariableAddressOffset);
        // 我们来玩compareAndSwapInt了
        boolean success = unsafe.compareAndSwapInt(instance, countVariableAddressOffset, 0, 1);
        System.out.println("CAS 成功:" + success);
        System.out.println("经过CAS,count值变为:" + instance.count);
    }
}

另外对于x86 CPU,CAS实际是一条名为“cmpxchg”的指令,了解即可。

最后希望这个回答你能彻底明白

0 回复 有任何疑惑可以回复我~
birdskyws 2018-07-12 16:27:59

感谢 shinysirius

学习的时候,也卡在这块了,老师没说清楚。看同学的解释大概是什么意思了。把var5交给底层判断,如果var5没变,那么改为var5+var4,即修改为目的结果。

0 回复 有任何疑惑可以回复我~
  • 我也这么理解的,是这样的么?
    回复 有任何疑惑可以回复我~ 2018-10-12 17:33:10
Jimin 2018-04-02 14:26:49

我看了一下视频,把传入的变量当时看错了,因此把var2的值说错了。

借这个问题,再总结一下:

compareAndSwapInt(var1, var2, var5, var5 + var4)换成 compareAndSwapInt(obj, offset, expect, update)能清楚一些,如果obj内的value和expect相等,就证明没有其他线程改变过这个变量,那么就更新它为update,如果这一步CAS没有成功,那就采用自旋的方式继续进行CAS操作。这块是一个CPU指令完成的,依旧是原子操作。

0 回复 有任何疑惑可以回复我~
  • 这个还是没有解释清楚!!!!
    回复 有任何疑惑可以回复我~ 2018-07-12 16:18:11
  • offset是什么含义,value,expect怎么计算的?如果上课讲不清楚,还要自己摸搜索很长时间,请慕课网认真负责。听课的时候因为老师不讲清楚会很着急!!!!
    回复 有任何疑惑可以回复我~ 2018-07-12 16:20:16
  • xring 回复 birdskyws #3
    以 AtomicInteger 为例,查看类定义文件,里面有两行定义:
    private static final long valueOffset;
    private volatile int value;
    
    valueOffset 为对象内的偏移量,是一个 final 的值(其实就是一个字段到对象头部的偏移量,通过这个偏移量可以快速定位字段)
    
    而 value 是指这个 AtomicInteger 类实例当前的值
    
    expect 是指 value + delta,即原来值加上变更值,如原来值是3 然后执行 getAndIncrement,这里 delta 就是1,如果调用方法 getAndAdd(int delta),则 delta 是传入的参数。
    回复 有任何疑惑可以回复我~ 2018-11-07 14:43:45
Jimin 2018-04-01 19:03:24

你好,你说的这个没问题,我现在不方便看视频,等我确认一下

0 回复 有任何疑惑可以回复我~
提问者 shinysirius 2018-04-01 19:00:23

这个地方今天看的时候困惑了半天

0 回复 有任何疑惑可以回复我~
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信