天天看点

Java Magic. Part 4: sun.misc.Unsafe

java是一门安全的编程语言,防止程序员犯很多愚蠢的错误,它们大部分是基于内存管理的。但是,有一种方式可以有意的执行一些不安全、容易犯错的操作,那就是使用<code>unsafe</code>类。

本文是<code>sun.misc.unsafe</code>公共api的简要概述,及其一些有趣的用法。

在使用unsafe之前,我们需要创建unsafe对象的实例。这并不像<code>unsafe unsafe = new unsafe()</code>这么简单,因为<code>unsafe的</code>构造器是私有的。它也有一个静态的<code>getunsafe()</code>方法,但如果你直接调用<code>unsafe.getunsafe()</code>,你可能会得到<code>securityexception异常。只能从受信任的代码中使用这个方法。</code>

<code>1</code>

<code>public</code> <code>static</code> <code>unsafe getunsafe() {</code>

<code>2</code>

<code>    </code><code>class cc = sun.reflect.reflection.getcallerclass(</code><code>2</code><code>);</code>

<code>3</code>

<code>    </code><code>if</code> <code>(cc.getclassloader() != </code><code>null</code><code>)</code>

<code>4</code>

<code>        </code><code>throw</code> <code>new</code> <code>securityexception(</code><code>"unsafe"</code><code>);</code>

<code>5</code>

<code>    </code><code>return</code> <code>theunsafe;</code>

<code>6</code>

<code>}</code>

这就是java如何验证代码是否可信。它只检查我们的代码是否由主要的类加载器加载。

我们可以令我们的代码“受信任”。运行程序时,使用bootclasspath 选项,指定系统类路径加上你使用的一个unsafe路径。

<code>java -xbootclasspath:/usr/jdk1.</code><code>7.0</code><code>/jre/lib/rt.jar:. com.mishadoff.magic.unsafeclient</code>

但这太难了。

<code>unsafe</code>类包含一个私有的、名为<code>theunsafe的实例</code>,我们可以通过java反射窃取该变量。

<code>field f = unsafe.</code><code>class</code><code>.getdeclaredfield(</code><code>"theunsafe"</code><code>);</code>

<code>f.setaccessible(</code><code>true</code><code>);</code>

<code>unsafe unsafe = (unsafe) f.get(</code><code>null</code><code>);</code>

注意:忽略你的ide。比如:eclipse显示”access restriction…”错误,但如果你运行代码,它将正常运行。如果这个错误提示令人烦恼,可以通过以下设置来避免:

<code>preferences -&gt; java -&gt; compiler -&gt; errors/warnings -&gt;</code>

<code>deprecated and restricted api -&gt; forbidden reference -&gt; warning</code>

sun.misc.unsafe类包含105个方法。实际上,对各种实体操作有几组重要方法,其中的一些如下:

info.仅返回一些低级的内存信息

<code>addresssize</code>

<code>pagesize</code>

objects.提供用于操作对象及其字段的方法

<code>allocateinstance</code>

<code>objectfieldoffset</code>

classes.提供用于操作类及其静态字段的方法

<code>staticfieldoffset</code>

<code>defineclass</code>

<code>defineanonymousclass</code>

<code>ensureclassinitialized</code>

arrays.操作数组

<code>arraybaseoffset</code>

<code>arrayindexscale</code>

synchronization.低级的同步原语

<code>monitorenter</code>

<code>trymonitorenter</code>

<code>monitorexit</code>

<code>compareandswapint</code>

<code>putorderedint</code>

memory.直接内存访问方法

<code>allocatememory</code>

<code>copymemory</code>

<code>freememory</code>

<code>getaddress</code>

<code>getint</code>

<code>putint</code>

避免初始化

当你想要跳过对象初始化阶段,或绕过构造器的安全检查,或实例化一个没有任何公共构造器的类,<code>allocateinstance</code>方法是非常有用的。考虑以下类:

<code>class</code> <code>a {</code>

<code>    </code><code>private</code> <code>long</code> <code>a; </code><code>// not initialized value</code>

<code>    </code><code>public</code> <code>a() {</code>

<code>        </code><code>this</code><code>.a = </code><code>1</code><code>; </code><code>// initialization</code>

<code>    </code><code>}</code>

<code>7</code>

<code>8</code>

<code>    </code><code>public</code> <code>long</code> <code>a() { </code><code>return</code> <code>this</code><code>.a; }</code>

<code>9</code>

使用构造器、反射和unsafe初始化它,将得到不同的结果。

<code>a o1 = </code><code>new</code> <code>a(); </code><code>// constructor</code>

<code>o1.a(); </code><code>// prints 1</code>

<code>a o2 = a.</code><code>class</code><code>.newinstance(); </code><code>// reflection</code>

<code>o2.a(); </code><code>// prints 1</code>

<code>a o3 = (a) unsafe.allocateinstance(a.</code><code>class</code><code>); </code><code>// unsafe</code>

<code>o3.a(); </code><code>// prints 0</code>

想想所有单例发生了什么。

内存崩溃(memory corruption)

这对于每个c程序员来说是常见的。顺便说一下,它是绕过安全的常用技术。

考虑下那些用于检查“访问规则”的简单类:

<code>class</code> <code>guard {</code>

<code>       </code><code>private</code> <code>int</code> <code>access_allowed = </code><code>1</code><code>;</code>

<code>       </code><code>public</code> <code>boolean</code> <code>giveaccess() {</code>

<code>              </code><code>return</code> <code>42</code> <code>== access_allowed;</code>

<code>       </code><code>}</code>

客户端代码是非常安全的,并且通过调用<code>giveaccess()</code>来检查访问规则。可惜,对于客户,它总是返回false。只有特权用户可以以某种方式改变<code>access_allowed</code>常量的值并且得到访问(giveaccess()方法返回true,译者注)。

实际上,这并不是真的。演示代码如下:

<code>guard guard = </code><code>new</code> <code>guard();</code>

<code>guard.giveaccess();   </code><code>// false, no access</code>

<code>// bypass</code>

<code>unsafe unsafe = getunsafe();</code>

<code>field f = guard.getclass().getdeclaredfield(</code><code>"access_allowed"</code><code>);</code>

<code>unsafe.putint(guard, unsafe.objectfieldoffset(f), </code><code>42</code><code>); </code><code>// memory corruption</code>

<code>guard.giveaccess(); </code><code>// true, access granted</code>

现在所有的客户都拥有无限制的访问权限。

实际上,反射可以实现相同的功能。但值得关注的是,我们可以修改任何对象,甚至没有这些对象的引用。

例如,有一个guard对象,所在内存中的位置紧接着在当前guard对象之后。我们可以用以下代码来修改它的<code>access_allowed</code>字段:

<code>unsafe.putint(guard, </code><code>16</code> <code>+ unsafe.objectfieldoffset(f), </code><code>42</code><code>); </code><code>// memory corruption</code>

注意:我们不必持有这个对象的引用。16是<code>guard</code>对象在32位架构上的大小。我们可以手工计算它,或者通过使用<code>sizeof</code>方法(它的定义,如下节)。

<code>01</code>

<code>public</code> <code>static</code> <code>long</code> <code>sizeof(object o) {</code>

<code>02</code>

<code>    </code><code>unsafe u = getunsafe();</code>

<code>03</code>

<code>    </code><code>hashset&lt;field&gt; fields = </code><code>new</code> <code>hashset&lt;field&gt;();</code>

<code>04</code>

<code>    </code><code>class c = o.getclass();</code>

<code>05</code>

<code>    </code><code>while</code> <code>(c != object.</code><code>class</code><code>) {</code>

<code>06</code>

<code>        </code><code>for</code> <code>(field f : c.getdeclaredfields()) {</code>

<code>07</code>

<code>            </code><code>if</code> <code>((f.getmodifiers() &amp; modifier.static) == </code><code>0</code><code>) {</code>

<code>08</code>

<code>                </code><code>fields.add(f);</code>

<code>09</code>

<code>            </code><code>}</code>

<code>10</code>

<code>        </code><code>}</code>

<code>11</code>

<code>        </code><code>c = c.getsuperclass();</code>

<code>12</code>

<code>13</code>

<code>14</code>

<code>    </code><code>// get offset</code>

<code>15</code>

<code>    </code><code>long</code> <code>maxsize = </code><code>0</code><code>;</code>

<code>16</code>

<code>    </code><code>for</code> <code>(field f : fields) {</code>

<code>17</code>

<code>        </code><code>long</code> <code>offset = u.objectfieldoffset(f);</code>

<code>18</code>

<code>        </code><code>if</code> <code>(offset &gt; maxsize) {</code>

<code>19</code>

<code>            </code><code>maxsize = offset;</code>

<code>20</code>

<code>21</code>

<code>22</code>

<code>23</code>

<code>    </code><code>return</code> <code>((maxsize/</code><code>8</code><code>) + </code><code>1</code><code>) * </code><code>8</code><code>;   </code><code>// padding</code>

<code>24</code>

算法如下:通过所有非静态字段(包含父类的),获取每个字段的偏移量(offset),找到偏移最大值并填充字节数(padding)。我可能错过一些东西,但思路是明确的。

如果我们仅读取对象的类结构大小值,sizeof的实现可以更简单,这位于<code>jvm 1.7 32 bit</code>中的偏移量12。

<code>public</code> <code>static</code> <code>long</code> <code>sizeof(object object){</code>

<code>    </code><code>return</code> <code>getunsafe().getaddress(</code>

<code>        </code><code>normalize(getunsafe().getint(object, 4l)) + 12l);</code>

<code>normalize</code>是一个为了正确内存地址使用,将有符号的int类型强制转换成无符号的long类型的方法。

<code>private</code> <code>static</code> <code>long</code> <code>normalize(</code><code>int</code> <code>value) {</code>

<code>    </code><code>if</code><code>(value &gt;= </code><code>0</code><code>) </code><code>return</code> <code>value;</code>

<code>    </code><code>return</code> <code>(~0l &gt;&gt;&gt; </code><code>32</code><code>) &amp; value;</code>

真棒,这个方法返回的结果与我们之前的sizeof方法一样。

实际上,对于良好、安全、准确的sizeof方法,最好使用 java.lang.instrument包,但这需要在jvm中指定<code>agent</code>选项。

浅拷贝(shallow copy)

为了实现计算对象自身内存大小,我们可以简单地添加拷贝对象方法。标准的解决方案是使用<code>cloneable</code>修改你的代码,或者在你的对象中实现自定义的拷贝方法,但它不会是多用途的方法。

浅拷贝:

<code>static</code> <code>object shallowcopy(object obj) {</code>

<code>    </code><code>long</code> <code>size = sizeof(obj);</code>

<code>    </code><code>long</code> <code>start = toaddress(obj);</code>

<code>    </code><code>long</code> <code>address = getunsafe().allocatememory(size);</code>

<code>    </code><code>getunsafe().copymemory(start, address, size);</code>

<code>    </code><code>return</code> <code>fromaddress(address);</code>

<code>toaddress和</code><code>fromaddress</code>将对象转换为其在内存中的地址,反之亦然。

<code>static</code> <code>long</code> <code>toaddress(object obj) {</code>

<code>    </code><code>object[] array = </code><code>new</code> <code>object[] {obj};</code>

<code>    </code><code>long</code> <code>baseoffset = getunsafe().arraybaseoffset(object[].</code><code>class</code><code>);</code>

<code>    </code><code>return</code> <code>normalize(getunsafe().getint(array, baseoffset));</code>

<code>static</code> <code>object fromaddress(</code><code>long</code> <code>address) {</code>

<code>    </code><code>object[] array = </code><code>new</code> <code>object[] {</code><code>null</code><code>};</code>

<code>    </code><code>getunsafe().putlong(array, baseoffset, address);</code>

<code>    </code><code>return</code> <code>array[</code><code>0</code><code>];</code>

这个拷贝方法可以用来拷贝任何类型的对象,动态计算它的大小。注意,在拷贝后,你需要将对象转换成特定的类型。

在<code>unsafe</code>中,一个更有趣的直接内存访问的用法是,从内存中删除不必要的对象。

检索用户密码的大多数api的签名为<code>byte[]</code>或<code>char[],</code>为什么是数组呢?

这完全是出于安全的考虑,因为我们可以删除不需要的数组元素。如果将用户密码检索成字符串,这可以像一个对象一样在内存中保存,而删除该对象只需执行解除引用的操作。但是,这个对象仍然在内存中,由gc决定的时间来执行清除。

创建具有相同大小、假的string对象,来取代在内存中原来的string对象的技巧:

<code>string password = </code><code>new</code> <code>string(</code><code>"l00k@myhor$e"</code><code>);</code>

<code>string fake = </code><code>new</code> <code>string(password.replaceall(</code><code>"."</code><code>, </code><code>"?"</code><code>));</code>

<code>system.out.println(password); </code><code>// l00k@myhor$e</code>

<code>system.out.println(fake); </code><code>// ????????????</code>

<code>getunsafe().copymemory(</code>

<code>          </code><code>fake, 0l, </code><code>null</code><code>, toaddress(password), sizeof(password));</code>

<code>system.out.println(password); </code><code>// ????????????</code>

感觉很安全。

修改:这并不安全。为了真正的安全,我们需要通过反射删除后台char数组:

<code>field stringvalue = string.</code><code>class</code><code>.getdeclaredfield(</code><code>"value"</code><code>);</code>

<code>stringvalue.setaccessible(</code><code>true</code><code>);</code>

<code>char</code><code>[] mem = (</code><code>char</code><code>[]) stringvalue.get(password);</code>

<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i &lt; mem.length; i++) {</code>

<code>  </code><code>mem[i] = </code><code>'?'</code><code>;</code>

感谢peter verhas指定出这一点。

java中没有多继承。

这是对的,除非我们可以将任意类型转换成我们想要的其他类型。

<code>long</code> <code>intclassaddress = normalize(getunsafe().getint(</code><code>new</code> <code>integer(</code><code>0</code><code>), 4l));</code>

<code>long</code> <code>strclassaddress = normalize(getunsafe().getint(</code><code>""</code><code>, 4l));</code>

<code>getunsafe().putaddress(intclassaddress + </code><code>36</code><code>, strclassaddress);</code>

这个代码片段将string类型添加到integer超类中,因此我们可以强制转换,且没有运行时异常。

<code>(string) (object) (</code><code>new</code> <code>integer(</code><code>666</code><code>))</code>

有一个问题,我们必须预先强制转换对象,以欺骗编译器。

我们可以在运行时创建一个类,比如从已编译的.class文件中。将类内容读取为字节数组,并正确地传递给<code>defineclass</code>方法。

<code>byte</code><code>[] classcontents = getclasscontent();</code>

<code>class c = getunsafe().defineclass(</code>

<code>              </code><code>null</code><code>, classcontents, </code><code>0</code><code>, classcontents.length);</code>

<code>    </code><code>c.getmethod(</code><code>"a"</code><code>).invoke(c.newinstance(), </code><code>null</code><code>); </code><code>// 1</code>

从定义文件(class文件)中读取(代码)如下:

<code>private</code> <code>static</code> <code>byte</code><code>[] getclasscontent() </code><code>throws</code> <code>exception {</code>

<code>    </code><code>file f = </code><code>new</code> <code>file(</code><code>"/home/mishadoff/tmp/a.class"</code><code>);</code>

<code>    </code><code>fileinputstream input = </code><code>new</code> <code>fileinputstream(f);</code>

<code>    </code><code>byte</code><code>[] content = </code><code>new</code> <code>byte</code><code>[(</code><code>int</code><code>)f.length()];</code>

<code>    </code><code>input.read(content);</code>

<code>    </code><code>input.close();</code>

<code>    </code><code>return</code> <code>content;</code>

当你必须动态创建类,而现有代码中有一些代理, 这是很有用的。

不喜欢受检异常?没问题。

<code>getunsafe().throwexception(</code><code>new</code> <code>ioexception());</code>

该方法抛出受检异常,但你的代码不必捕捉或重新抛出它,正如运行时异常一样。

这更有实用性。

大家都知道,标准java的<code>serializable的序列化能力是非常慢的。它同时要求类必须有一个公共的、无参数的构造器。</code>

<code>externalizable</code>比较好,但它需要定义类序列化的模式。

流行的高性能库,比如kryo具有依赖性,这对于低内存要求来说是不可接受的。

unsafe类可以很容易实现完整的序列化周期。

序列化:

使用反射构建模式对象,类只可做一次。

使用<code>unsafe</code>方法,如<code>getlong</code>、<code>getint</code>、<code>getobject</code>等来检索实际字段值。

添加类标识,以便有能力恢复该对象

将它们写入文件或任意输出

你也可以添加压缩(步骤)以节省空间。

反序列化:

创建已序列化对象实例,使用<code>allocateinstance</code>协助(即可),因为不需要任何构造器。

构建模式,与序列化的步骤1相同。

从文件或任意输入中读取所有字段。

使用<code>unsafe</code>方法,如<code>putlong</code>、<code>putint</code>、<code>putobject</code>等来填充该对象。

实际上,在正确的实现过程中还有更多的细节,但思路是明确的。

这个序列化将非常快。

正如你所知,java数组大小的最大值为<code>integer.max_value</code>。使用直接内存分配,我们创建的数组大小受限于堆大小。

<code>superarray的实现</code>:

<code>class</code> <code>superarray {</code>

<code>    </code><code>private</code> <code>final</code> <code>static</code> <code>int</code> <code>byte = </code><code>1</code><code>;</code>

<code>    </code><code>private</code> <code>long</code> <code>size;</code>

<code>    </code><code>private</code> <code>long</code> <code>address;</code>

<code>    </code><code>public</code> <code>superarray(</code><code>long</code> <code>size) {</code>

<code>        </code><code>this</code><code>.size = size;</code>

<code>        </code><code>address = getunsafe().allocatememory(size * byte);</code>

<code>    </code><code>public</code> <code>void</code> <code>set(</code><code>long</code> <code>i, </code><code>byte</code> <code>value) {</code>

<code>        </code><code>getunsafe().putbyte(address + i * byte, value);</code>

<code>    </code><code>public</code> <code>int</code> <code>get(</code><code>long</code> <code>idx) {</code>

<code>        </code><code>return</code> <code>getunsafe().getbyte(address + idx * byte);</code>

<code>    </code><code>public</code> <code>long</code> <code>size() {</code>

<code>        </code><code>return</code> <code>size;</code>

简单用法:

<code>long</code> <code>super_size = (</code><code>long</code><code>)integer.max_value * </code><code>2</code><code>;</code>

<code>superarray array = </code><code>new</code> <code>superarray(super_size);</code>

<code>system.out.println(</code><code>"array size:"</code> <code>+ array.size()); </code><code>// 4294967294</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; </code><code>100</code><code>; i++) {</code>

<code>    </code><code>array.set((</code><code>long</code><code>)integer.max_value + i, (</code><code>byte</code><code>)</code><code>3</code><code>);</code>

<code>    </code><code>sum += array.get((</code><code>long</code><code>)integer.max_value + i);</code>

<code>system.out.println(</code><code>"sum of 100 elements:"</code> <code>+ sum);  </code><code>// 300</code>

实际上,这是堆外内存(<code>off-heap memory</code>)技术,在<code>java.nio</code>包中部分可用。

这种方式的内存分配不在堆上,且不受gc管理,所以必须小心<code>unsafe.freememory()的使用。它也不执行任何边界检查,所以任何非法访问可能会导致jvm崩溃。</code>

这可用于数学计算,代码可操作大数组的数据。此外,这可引起实时程序员的兴趣,可打破gc在大数组上延迟的限制。

几句关于<code>unsafe</code>的并发性。<code>compareandswap</code>方法是原子的,并且可用来实现高性能的、无锁的数据结构。

比如,考虑问题:在使用大量线程的共享对象上增长值。

首先,我们定义简单的<code>counter</code>接口:

<code>interface</code> <code>counter {</code>

<code>    </code><code>void</code> <code>increment();</code>

<code>    </code><code>long</code> <code>getcounter();</code>

然后,我们定义使用counter的工作线程<code>counterclient</code>:

<code>class</code> <code>counterclient </code><code>implements</code> <code>runnable {</code>

<code>    </code><code>private</code> <code>counter c;</code>

<code>    </code><code>private</code> <code>int</code> <code>num;</code>

<code>    </code><code>public</code> <code>counterclient(counter c, </code><code>int</code> <code>num) {</code>

<code>        </code><code>this</code><code>.c = c;</code>

<code>        </code><code>this</code><code>.num = num;</code>

<code>    </code><code>@override</code>

<code>    </code><code>public</code> <code>void</code> <code>run() {</code>

<code>        </code><code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; num; i++) {</code>

<code>            </code><code>c.increment();</code>

测试代码:

<code>int</code> <code>num_of_threads = </code><code>1000</code><code>;</code>

<code>int</code> <code>num_of_increments = </code><code>100000</code><code>;</code>

<code>executorservice service = executors.newfixedthreadpool(num_of_threads);</code>

<code>counter counter = ... </code><code>// creating instance of specific counter</code>

<code>long</code> <code>before = system.currenttimemillis();</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; num_of_threads; i++) {</code>

<code>    </code><code>service.submit(</code><code>new</code> <code>counterclient(counter, num_of_increments));</code>

<code>service.shutdown();</code>

<code>service.awaittermination(</code><code>1</code><code>, timeunit.minutes);</code>

<code>long</code> <code>after = system.currenttimemillis();</code>

<code>system.out.println(</code><code>"counter result: "</code> <code>+ c.getcounter());</code>

<code>system.out.println(</code><code>"time passed in ms:"</code> <code>+ (after - before));</code>

第一个无锁版本的计数器:

<code>class</code> <code>stupidcounter </code><code>implements</code> <code>counter {</code>

<code>    </code><code>private</code> <code>long</code> <code>counter = </code><code>0</code><code>;</code>

<code>    </code><code>public</code> <code>void</code> <code>increment() {</code>

<code>        </code><code>counter++;</code>

<code>    </code><code>public</code> <code>long</code> <code>getcounter() {</code>

<code>        </code><code>return</code> <code>counter;</code>

输出:

<code>counter result: </code><code>99542945</code>

<code>time passed in ms: </code><code>679</code>

运行快,但没有线程管理,结果是不准确的。第二次尝试,添加上最简单的java式同步:

<code>class</code> <code>synccounter </code><code>implements</code> <code>counter {</code>

<code>    </code><code>public</code> <code>synchronized</code> <code>void</code> <code>increment() {</code>

<code>counter result: </code><code>100000000</code>

<code>time passed in ms: </code><code>10136</code>

激进的同步有效,但耗时长。试试<code>reentrantreadwritelock</code>:

<code>class</code> <code>lockcounter </code><code>implements</code> <code>counter {</code>

<code>    </code><code>private</code> <code>writelock lock = </code><code>new</code> <code>reentrantreadwritelock().writelock();</code>

<code>        </code><code>lock.lock();</code>

<code>        </code><code>lock.unlock();</code>

<code>time passed in ms: </code><code>8065</code>

仍然正确,耗时较短。atomics的运行效果如何?

<code>class</code> <code>atomiccounter </code><code>implements</code> <code>counter {</code>

<code>    </code><code>atomiclong counter = </code><code>new</code> <code>atomiclong(</code><code>0</code><code>);</code>

<code>        </code><code>counter.incrementandget();</code>

<code>        </code><code>return</code> <code>counter.get();</code>

<code>time passed in ms: </code><code>6552</code>

<code>atomiccounter的运行结果更好。最后,试试</code><code>unsafe</code>原始的<code>compareandswaplong</code>,看看它是否真的只有特权才能使用它?

<code>class</code> <code>cascounter </code><code>implements</code> <code>counter {</code>

<code>    </code><code>private</code> <code>volatile</code> <code>long</code> <code>counter = </code><code>0</code><code>;</code>

<code>    </code><code>private</code> <code>unsafe unsafe;</code>

<code>    </code><code>private</code> <code>long</code> <code>offset;</code>

<code>    </code><code>public</code> <code>cascounter() </code><code>throws</code> <code>exception {</code>

<code>        </code><code>unsafe = getunsafe();</code>

<code>        </code><code>offset = unsafe.objectfieldoffset(cascounter.</code><code>class</code><code>.getdeclaredfield(</code><code>"counter"</code><code>));</code>

<code>        </code><code>long</code> <code>before = counter;</code>

<code>        </code><code>while</code> <code>(!unsafe.compareandswaplong(</code><code>this</code><code>, offset, before, before + </code><code>1</code><code>)) {</code>

<code>            </code><code>before = counter;</code>

<code>time passed in ms: </code><code>6454</code>

看起来似乎等价于atomics。atomics使用<code>unsafe</code>?(是的)

实际上,这个例子很简单,但它展示了<code>unsafe</code>的一些能力。

如我所说,cas原语可以用来实现无锁的数据结构。背后的原理很简单:

有一些状态

创建它的副本

修改它

执行cas

如果失败,重复尝试

实际上,现实中比你现象的更难。存在着许多问题,如aba问题、指令重排序等。

修改:给counter变量添加<code>volatile</code>关键字,以避免无限循环的风险。

即使<code>unsafe</code>对应用程序很有用,但(建议)不要使用它。