BAEL-814 unsafe (#1645)

* code for the unsafe article

* more descriptive example

* proper eng

* better test name

* free memory call

* java 8 style
This commit is contained in:
Tomasz Lelek 2017-04-15 17:34:56 +02:00 committed by pedja4
parent 2dbca951cb
commit 5e0d1e88ef
3 changed files with 190 additions and 0 deletions

View File

@ -0,0 +1,33 @@
package com.baeldung.unsafe;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
class CASCounter {
private final Unsafe unsafe;
private volatile long counter = 0;
private long offset;
private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
}
public CASCounter() throws Exception {
unsafe = getUnsafe();
offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
}
public void increment() {
long before = counter;
while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
before = counter;
}
}
public long getCounter() {
return counter;
}
}

View File

@ -0,0 +1,39 @@
package com.baeldung.unsafe;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
class OffHeapArray {
private final static int BYTE = 1;
private long size;
private long address;
private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
}
public OffHeapArray(long size) throws NoSuchFieldException, IllegalAccessException {
this.size = size;
address = getUnsafe().allocateMemory(size * BYTE);
}
public void set(long i, byte value) throws NoSuchFieldException, IllegalAccessException {
getUnsafe().putByte(address + i * BYTE, value);
}
public int get(long idx) throws NoSuchFieldException, IllegalAccessException {
return getUnsafe().getByte(address + idx * BYTE);
}
public long size() {
return size;
}
public void freeMemory() throws NoSuchFieldException, IllegalAccessException {
getUnsafe().freeMemory(address);
}
}

View File

@ -0,0 +1,118 @@
package com.baeldung.unsafe;
import org.junit.Before;
import org.junit.Test;
import sun.misc.Unsafe;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;
public class UnsafeTest {
private Unsafe unsafe;
@Before
public void setup() throws NoSuchFieldException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
}
@Test
public void givenClass_whenInitializeIt_thenShouldHaveDifferentStateWhenUseUnsafe() throws IllegalAccessException, InstantiationException {
//when
InitializationOrdering o1 = new InitializationOrdering();
assertEquals(o1.getA(), 1);
//when
InitializationOrdering o3 = (InitializationOrdering) unsafe.allocateInstance(InitializationOrdering.class);
assertEquals(o3.getA(), 0);
}
@Test
public void givenPrivateMethod_whenUsingUnsafe_thenCanModifyPrivateField() throws NoSuchFieldException {
//given
SecretHolder secretHolder = new SecretHolder();
//when
Field f = secretHolder.getClass().getDeclaredField("SECRET_VALUE");
unsafe.putInt(secretHolder, unsafe.objectFieldOffset(f), 1);
//then
assertTrue(secretHolder.secretIsDisclosed());
}
@Test(expected = IOException.class)
public void givenUnsafeThrowException_whenThrowCheckedException_thenNotNeedToCatchIt() {
unsafe.throwException(new IOException());
}
@Test
public void givenArrayBiggerThatMaxInt_whenAllocateItOffHeapMemory_thenSuccess() throws NoSuchFieldException, IllegalAccessException {
//given
long SUPER_SIZE = (long) Integer.MAX_VALUE * 2;
OffHeapArray array = new OffHeapArray(SUPER_SIZE);
//when
int sum = 0;
for (int i = 0; i < 100; i++) {
array.set((long) Integer.MAX_VALUE + i, (byte) 3);
sum += array.get((long) Integer.MAX_VALUE + i);
}
//then
assertEquals(array.size(), SUPER_SIZE);
assertEquals(sum, 300);
}
@Test
public void givenUnsafeCompareAndSwap_whenUseIt_thenCounterYildCorrectLockFreeResults() throws Exception {
//given
int NUM_OF_THREADS = 1_000;
int NUM_OF_INCREMENTS = 10_000;
ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREADS);
CASCounter casCounter = new CASCounter();
//when
IntStream.rangeClosed(0, NUM_OF_THREADS - 1)
.forEach(i -> service.submit(() -> IntStream
.rangeClosed(0, NUM_OF_INCREMENTS - 1)
.forEach(j -> casCounter.increment())));
service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
//then
assertEquals(NUM_OF_INCREMENTS * NUM_OF_THREADS, casCounter.getCounter());
}
class InitializationOrdering {
private long a;
public InitializationOrdering() {
this.a = 1;
}
public long getA() {
return this.a;
}
}
class SecretHolder {
private int SECRET_VALUE = 0;
public boolean secretIsDisclosed() {
return SECRET_VALUE == 1;
}
}
}