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:
parent
2dbca951cb
commit
5e0d1e88ef
33
core-java/src/test/java/com/baeldung/unsafe/CASCounter.java
Normal file
33
core-java/src/test/java/com/baeldung/unsafe/CASCounter.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
118
core-java/src/test/java/com/baeldung/unsafe/UnsafeTest.java
Normal file
118
core-java/src/test/java/com/baeldung/unsafe/UnsafeTest.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user