ABOUT ME

IT 블로그

Today
Yesterday
Total
  • [Java9] Variable Handles
    Java/기본 2018. 5. 2. 21:46
    728x90
    반응형

    개요

    java.util.concurrent.atomic이나 sun.misc.Unsafe을 이용하여 값들을 읽기/쓰기 했었는데, 보다 일관적인 방법을 제공한다.

    목적

    • 안정성:  유효한 메모리 바운더리안에서 사용
    • 무결정: final 필드 값을 업데이트 할 수 없음
    • 성능: sun.misc.Unsafe보다 성능이 비슷하거나 좋음
    • 사용성: sun.misc.Unsafe API, java.util.concurrent.atomic API보다 사용하기 좋음

    자극

      자바에서 동시 및 병렬 프로그래밍이 할 일이 많아졌다. counter 값을 원자적으로(thread safe하게) 변경 하기 위해서는 AtomicInteger(공간문제나 추가 작업등으로 오버헤드가 큼)나 sun.misc.Unsafe API(안전하지 않고 이식성이 안좋음)을 사용해야 했다. 이런 원자적인 값을 다루는 데 일관된 사용에 편리함을 줄 것이다.

    설명

    • variable handle은 다양한 access mode에서 변수에 대한 읽기/쓰기를 제공하는 참조(reference)
    • 대상: instance field, static field, array, off-heap regions(ByteBuffers) ...
    • library enhancements, JVM enhancements, compiler support가 필요함

    Memory Ordering

     Memory Ordering

     Access 

     Description 

     plain 

     read/write

     레퍼런스와 32bit 이하의 원시자료형 값에 대해서만 atomic함

     volatile 

     read/write

     메모리에서 데이터를 읽고 씀

     opaque 

     read/write

     같은 변수들에 대해서만 atomic함

     acquire 

     read

     이 액세스 전에 후속 로드 및 저장소의 순서가 변경되지 않음

     release

     write 

     이 액세스 후 이전 로드 및 저장소의 순서가 변경되지 않음


    C/C++11의 atomic와 대응됨


    Memory Fence Methods

     Method

    Operations Before Fence 

    Operations After Fence 

     void fullFence()

     loads and stores

     loads and stores  

     void acquireFence()

     loads 

     loads and stores 

     void releaseFence()

     loads and stores

     stores 

     void loadLoadFence()

     loads 

     loads 

     void storeStoreFence()

     stores 

     stores 

    C/C++11의 atomic_thread_fence와 대응됨


    코드


    package java9.book;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.VarHandle;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    public class VarHandleTest {
    public static class HandleTarget {
    public int count = 1;
    public String[] names = new String[]{"Alex", "Bob", "David"};
    public byte[] data = new byte[]{1, 0, 0, 0, 1, 0, 0, 0};
    public ByteBuffer dataBuffer = ByteBuffer.wrap(this.data);
    }
    private HandleTarget handleTarget;
    private VarHandle varHandle;
    @BeforeEach
    public void setUp() throws Exception {
    this.handleTarget = new HandleTarget();
    this.varHandle = MethodHandles
    .lookup()
    .findVarHandle(HandleTarget.class, "count", int.class);
    }
    /**
    * Read Access
    */
    @Test
    public void testGet() {
    assertEquals(1, this.varHandle.get(this.handleTarget));
    assertEquals(1, this.varHandle.getVolatile(this.handleTarget));
    assertEquals(1, this.varHandle.getOpaque(this.handleTarget));
    assertEquals(1, this.varHandle.getAcquire(this.handleTarget));
    }
    /**
    * Write Access
    */
    @Test
    public void testSet() {
    final int newValue = 2;
    this.varHandle.set(this.handleTarget, newValue);
    assertEquals(newValue, this.varHandle.get(this.handleTarget));
    this.varHandle.setVolatile(this.handleTarget, newValue + 1);
    assertEquals(newValue + 1, this.varHandle.get(this.handleTarget));
    this.varHandle.setOpaque(this.handleTarget, newValue + 2);
    assertEquals(newValue + 2, this.varHandle.get(this.handleTarget));
    this.varHandle.setRelease(this.handleTarget, newValue + 3);
    assertEquals(newValue + 3, this.varHandle.get(this.handleTarget));
    }
    /**
    * Atomic Update
    */
    @Test
    public void testAtomicUpdate() {
    final int expectedValue = 1;
    final int newValue = 2;
    assertTrue(this.varHandle.compareAndSet(this.handleTarget, expectedValue, newValue));
    assertEquals(newValue, this.varHandle.compareAndExchange(this.handleTarget, newValue, newValue + 1));
    assertEquals(newValue + 1, this.varHandle.getAndSet(this.handleTarget, newValue + 2));
    }
    /**
    * Numeric Atomic Update
    */
    @Test
    public void testNumericAtomicUpdate() {
    final int expectedValue = 1;
    assertEquals(expectedValue,
    this.varHandle.getAndAdd(this.handleTarget, 1));
    assertEquals(expectedValue + 1,
    this.varHandle.getAndAddAcquire(this.handleTarget, 1));
    assertEquals(expectedValue + 2,
    this.varHandle.getAndAddRelease(this.handleTarget, 1));
    }
    /**
    * Bitwise Atomic Update
    */
    @Test
    public void testBitwiseAtomicUpdate() {
    final int mask = 1;
    assertEquals(1, this.varHandle.getAndBitwiseAnd(this.handleTarget, mask));
    assertEquals(1, this.varHandle.get(this.handleTarget));
    assertEquals(1, this.varHandle.getAndBitwiseOr(this.handleTarget, mask));
    assertEquals(1, this.varHandle.get(this.handleTarget));
    assertEquals(1, this.varHandle.getAndBitwiseXor(this.handleTarget, mask));
    assertEquals(0, this.varHandle.get(this.handleTarget));
    }
    /**
    * Arrays
    */
    @Test
    public void testArray() {
    final VarHandle arrayElementHandle = MethodHandles.arrayElementVarHandle(String[].class);
    assertTrue(arrayElementHandle.compareAndSet(
    this.handleTarget.names, 0, "Alex", "Alex_new"));
    assertEquals("Alex_new", this.handleTarget.names[0]);
    }
    /**
    * byte[] View
    */
    @Test
    public void testByteArrayView() {
    final VarHandle varHandle = MethodHandles
    .byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN);
    final byte[] data = this.handleTarget.data;
    assertEquals(16777216, varHandle.get(data, 0));
    assertEquals(1, varHandle.get(data, 1));
    assertEquals(256, varHandle.get(data, 2));
    assertEquals(65536, varHandle.get(data, 3));
    assertEquals(16777216, varHandle.get(data, 4));
    }
    /**
    * ByteBuffer View
    */
    @Test
    public void testByteBufferView() {
    final VarHandle varHandle = MethodHandles
    .byteBufferViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN);
    final ByteBuffer dataBuffer = this.handleTarget.dataBuffer;
    assertEquals(16777216, varHandle.get(dataBuffer, 0));
    assertEquals(1, varHandle.get(dataBuffer, 1));
    assertEquals(256, varHandle.get(dataBuffer, 2));
    assertEquals(65536, varHandle.get(dataBuffer, 3));
    assertEquals(16777216, varHandle.get(dataBuffer, 4));
    }
    }
    // Exploring Java 9, p.106


    같이 알면 좋은 지식들


    관련된 한글 자료가 없어서 힘들다...



    참고


    반응형

    'Java > 기본' 카테고리의 다른 글

    댓글

Designed by Tistory.