programing

Java에서 Atomic Reference를 사용하는 경우

prostudy 2022. 8. 27. 08:48
반응형

Java에서 Atomic Reference를 사용하는 경우

언제 사용합니까?

모든 멀티스레드 프로그램에서 개체를 생성해야 합니까?

AtomicReference를 사용해야 하는 간단한 예를 제공합니다.

아토믹 레퍼런스는, 모니터 베이스의 동기화가 적절하지 않은 레퍼런스에서 단순한 아토믹(, 스레드 세이프, 중요하지 않은) 조작을 실시할 필요가 있는 설정으로 사용할 필요가 있습니다.오브젝트 상태가 마지막으로 체크한 대로 유지되는 경우에만 특정 필드를 체크한다고 가정합니다.

AtomicReference<Object> cache = new AtomicReference<Object>();

Object cachedValue = new Object();
cache.set(cachedValue);

//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);

에, 이것은, 「아토믹 레퍼런스」의으로서 「아토믹 레퍼런스」의 의미론입니다.cache는 스레드됩니다.synchronized나 일반 or or synchron synchron를 사용하는 것이 java.util.concurrent이 틀Atomic*네가 뭘 하는지 모르는 이상 말이야

이 토픽을 소개하기 위한 두 가지 뛰어난 데드 트리 레퍼런스가 있습니다.

참조 할당(이것이 항상 사실인지는 알 수 없습니다)에 주의해 주십시오.=는 그 자체가 아토믹입니다(다음과 같은 원시 64비트 타입은 필요 없음).long ★★★★★★★★★★★★★★★★★」doubleatomic은 아닐 수 있지만 참조 업데이트는 항상 atomic입니다(비록 64비트라도).Atomic*.
Java Language Specification 3ed 섹션 17.7을 참조하십시오.

원자 참조는 여러 스레드 간에 불변 객체의 상태를 공유하고 변경해야 할 때 사용하는 것이 이상적입니다.그것은 매우 조밀한 문장이기 때문에 나는 그것을 조금 분해할 것이다.

첫째, 불변의 물체는 건설 후에도 효과적으로 변경되지 않는 물체이다.종종 불변 객체의 메서드는 동일한 클래스의 새 인스턴스를 반환합니다.예를 들어 Long과 Double의 래퍼 클래스와 String이 있습니다(JVM 불변 객체의 Programming Concurnity는 현대 동시성의 중요한 부분입니다).

다음으로 공유값을 공유하는 휘발성 객체보다 Atomic Reference가 좋은 이유.간단한 코드 예는 차이를 보여줍니다.

volatile String sharedValue;
static final Object lock=new Object();
void modifyString(){
  synchronized(lock){
    sharedValue=sharedValue+"something to add";
  }
}

현재 값을 기준으로 해당 휘발성 필드에서 참조하는 문자열을 수정할 때마다 먼저 해당 개체에 대한 잠금을 가져와야 합니다.그러면 중간에 다른 스레드가 들어오거나 새 문자열 연결 중간에 값이 변경되지 않습니다.그런 다음 스레드가 재개되면 다른 스레드의 작업을 중단합니다.하지만 솔직히 그 코드는 작동될 것이고, 깨끗해 보이고, 대부분의 사람들을 행복하게 할 것이다.

약간의 문제.느리다.특히 잠금 객체에 대한 경합이 심한 경우.이는 대부분의 잠금이 OS 시스템콜을 필요로 하고 스레드가 차단되어 다른 프로세스로 대체되도록 CPU에서 컨텍스트 전환되기 때문입니다.

다른 옵션은 Atomic Reference를 사용하는 것입니다.

public static AtomicReference<String> shared = new AtomicReference<>();
String init="Inital Value";
shared.set(init);
//now we will modify that value
boolean success=false;
while(!success){
  String prevValue=shared.get();
  // do all the work you need to
  String newValue=shared.get()+"lets add something";
  // Compare and set
  success=shared.compareAndSet(prevValue,newValue);
}

왜 이게 더 나을까?솔직히 그 코드는 예전보다 조금 덜 깨끗하다.하지만 Atomic Reference에서 정말 중요한 일이 벌어지고 있습니다. 그것은 비교와 교환입니다.스위치를 실행하는 것은 OS 콜이 아닌 단일 CPU 명령입니다.이것은 CPU에 대한 단일 명령입니다.또한 잠금이 없기 때문에 잠금이 작동될 경우 컨텍스트 스위치가 없기 때문에 더 많은 시간을 절약할 수 있습니다.

AtomicReferences의 경우 .timeout() 호출을 사용하지 않고 예상 값을 == 비교합니다.따라서 예상되는 객체가 get in loop에서 반환된 실제 객체인지 확인합니다.

다음으로 ATOMIC Reference의 사용 예를 나타냅니다.

이 클래스는 번호 범위로 동작하며 개개의 AtmomicInteger 변수를 사용하여 하한과 상한을 유지합니다.

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

    public void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get())
            throw new IllegalArgumentException(
                    "can't set lower to " + i + " > upper");
        lower.set(i);
    }

    public void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get())
            throw new IllegalArgumentException(
                    "can't set upper to " + i + " < lower");
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

setLower와 setUpper는 모두 체크텐액트 시퀀스이지만 원자성이 되기 위해 충분한 잠금을 사용하지 않습니다.번호 범위가 유지(0, 10)되어 있고, 1개의 스레드가 setLower(5)를 호출하고, 다른 스레드가 setUpper(4)를 호출하는 경우 타이밍이 좋지 않은 경우, 양쪽 모두 세터의 체크를 통과하고, 양쪽의 변경이 적용됩니다.그 결과, 범위가 무효 상태(5, 4)로 유지됩니다.따라서 기본 ATOMIC Integer는 스레드 세이프이지만 복합 클래스는 그렇지 않습니다.이는 상한 및 하한에 개별 AtomicIntegers를 사용하는 대신 AtomicReference를 사용하여 수정할 수 있습니다.

public class CasNumberRange {
    // Immutable
    private static class IntPair {
        final int lower;  // Invariant: lower <= upper
        final int upper;

        private IntPair(int lower, int upper) {
            this.lower = lower;
            this.upper = upper;
        }
    }

    private final AtomicReference<IntPair> values = 
            new AtomicReference<IntPair>(new IntPair(0, 0));

    public int getLower() {
        return values.get().lower;
    }

    public void setLower(int lower) {
        while (true) {
            IntPair oldv = values.get();
            if (lower > oldv.upper)
                throw new IllegalArgumentException(
                    "Can't set lower to " + lower + " > upper");
            IntPair newv = new IntPair(lower, oldv.upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }

    public int getUpper() {
        return values.get().upper;
    }

    public void setUpper(int upper) {
        while (true) {
            IntPair oldv = values.get();
            if (upper < oldv.lower)
                throw new IllegalArgumentException(
                    "Can't set upper to " + upper + " < lower");
            IntPair newv = new IntPair(oldv.lower, upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }
}

최적 잠금을 적용할 때 AtomicReference를 사용할 수 있습니다.공유 개체가 있으며 둘 이상의 스레드에서 개체를 변경하려고 합니다.

  1. 공유 객체의 복사본을 생성할 수 있습니다.
  2. 공유 개체 수정
  3. 공유 오브젝트가 이전과 동일한지 확인해야 합니다.그렇다면 수정된 복사본의 참조로 업데이트하십시오.

다른 스레드가 수정되었을 가능성이 있기 때문에 이 두 단계 사이에서 수정할 수 있습니다.원자폭탄 작전에서 해야 합니다.여기서 Atomic Reference가 도움이 될 수 있습니다.

여기 매우 간단한 사용 사례가 있습니다. 스레드 안전과는 아무런 관련이 없습니다.

람다 호출 간에 개체를 공유하는 방법은 다음과 같습니다.

public void doSomethingUsingLambdas() {

    AtomicReference<YourObject> yourObjectRef = new AtomicReference<>();

    soSomethingThatTakesALambda(() -> {
        yourObjectRef.set(youObject);
    });

    soSomethingElseThatTakesALambda(() -> {
        YourObject yourObject = yourObjectRef.get();
    });
}

이라든가 그런 건 () 람다, 그 때 (사소한 예시에 불과합니다), (사소한 예시에 불과합니다), (사소한 예시에 불과합니다)AtomicReference션입니니다다

실제로 하나의 항목만 있는 컬렉션을 포함하여 참조가 있는 모든 개체를 사용할 수 있습니다.단, Atomic Reference는 딱 맞습니다.

Atomic Reference는 언제 사용됩니까?

Atomic Reference는 동기화를 사용하지 않고 변수 값을 원자적으로 업데이트하는 유연한 방법입니다.

AtomicReference단일 변수에 대해 잠금 없는 스레드 세이프 프로그래밍을 지원합니다.

높은 수준의 동시 API로 스레드 안전을 달성하는 방법은 여러 가지가 있습니다.원자 변수는 여러 옵션 중 하나입니다.

Lock오브젝트는 많은 동시 응용 프로그램을 단순화하는 잠금 숙어를 지원합니다.

Executors스레드 시작 및 관리를 위한 고급 API를 정의합니다.java되는 이그제큐티브 합니다.java.util.concurrent.

동시 수집을 통해 대량의 데이터 수집을 보다 쉽게 관리할 수 있으며 동기화 필요성을 크게 줄일 수 있습니다.

원자 변수에는 동기화를 최소화하고 메모리 일관성 오류를 방지하는 기능이 있습니다.

AtomicReference를 사용해야 하는 간단한 예를 제공합니다.

「 」의 샘플 AtomicReference:

String initialReference = "value 1";

AtomicReference<String> someRef =
    new AtomicReference<String>(initialReference);

String newReference = "value 2";
boolean exchanged = someRef.compareAndSet(initialReference, newReference);
System.out.println("exchanged: " + exchanged);

모든 멀티스레드 프로그램에서 개체를 생성해야 합니까?

AtomicReference모든 멀티 스레드 프로그램에서 사용할 수 있습니다.

하려면 , 「」를 합니다.AtomicReference하려면 , 다른 Lock/synchronizedsyslog.

난 말을 많이 안 할 거야.이미 나의 존경하는 동료들은 그들의 귀중한 의견을 주었다.이 블로그의 마지막 부분에 있는 완전한 실행 코드를 통해 혼란을 해소할 수 있습니다. 멀티 스레드 시나리오에서 작은 프로그램을 예약하는 영화 좌석에 관한 것이다.

몇 가지 중요한 기본 사실은 다음과 같다.1> 다른 스레드는 히프 공간에서 예를 들어 스태틱멤버 변수와 경합할 수 있습니다.2 > 휘발성 읽기 또는 쓰기는 완전히 원자적이고 시리얼화/실행되며 메모리에서만 실행됩니다.이것을 말하는 것은, 모든 읽기가 이전의 메모리 기입 후에 행해진다는 것을 의미합니다.모든 쓰기는 메모리에서 이전에 읽은 내용을 따릅니다.따라서 휘발성으로 동작하는 스레드에는 항상 최신 값이 표시됩니다.Atomic Reference는 이 휘발성 속성을 사용합니다.

다음은 Atomic Reference의 소스 코드입니다.Atomic Reference는 객체 참조를 참조합니다.이 참조는 다음과 같이 AtomicReference 인스턴스의 휘발성 멤버 변수입니다.

private volatile V value;

get()은 단순히 변수의 최신 값을 반환합니다("volatiles before" 방식으로 volatile이 반환됨).

public final V get()

다음은 AtomicReference의 가장 중요한 메서드입니다.

public final boolean  compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

compareAndSet(expect, update) 메서드는 안전하지 않은 Java 클래스의 compareAndSwapObject() 메서드를 호출합니다.안전하지 않은 이 메서드콜은 네이티브콜을 호출하여 프로세서에 대한 단일 명령을 호출합니다."syslog" 및 "update"는 각각 개체를 참조합니다.

AtomicReference 인스턴스 멤버 변수 "value"가 동일한 개체를 참조하는 경우에만 "expect"에 의해 "update"가 이 인스턴스 변수에 할당되고 "true"가 반환됩니다.그렇지 않으면 false가 반환됩니다.모든 것이 원자적으로 이루어집니다.그 사이에 다른 스레드는 대행 수신할 수 없습니다.이것은 단일 프로세서 동작(현대 컴퓨터 아키텍처의 마법)이기 때문에 동기화된 블록을 사용하는 것보다 더 빠른 경우가 많습니다.그러나 여러 변수를 원자적으로 업데이트해야 할 경우 Atomic Reference는 도움이 되지 않습니다.

완전한 실행 코드를 추가하고 싶습니다.이 코드는 이클립스에서 실행할 수 있습니다.그렇게 하면 많은 혼란이 해소될 것이다.여기 22명의 사용자(MyTh 스레드)가 20개의 좌석을 예약하려고 합니다.다음은 코드 스니펫에 이어 코드 전체를 나타냅니다.

22명의 사용자가 20개의 좌석을 예약하려고 하는 코드 조각.

for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }

다음은 실행 중인 전체 코드입니다.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class Solution {

    static List<AtomicReference<Integer>> seats;// Movie seats numbered as per
                                                // list index

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        seats = new ArrayList<>();
        for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }
        for (Thread t : ths) {
            t.join();
        }
        for (AtomicReference<Integer> seat : seats) {
            System.out.print(" " + seat.get());
        }
    }

    /**
     * id is the id of the user
     * 
     * @author sankbane
     *
     */
    static class MyTh extends Thread {// each thread is a user
        static AtomicInteger full = new AtomicInteger(0);
        List<AtomicReference<Integer>> l;//seats
        int id;//id of the users
        int seats;

        public MyTh(List<AtomicReference<Integer>> list, int userId) {
            l = list;
            this.id = userId;
            seats = list.size();
        }

        @Override
        public void run() {
            boolean reserved = false;
            try {
                while (!reserved && full.get() < seats) {
                    Thread.sleep(50);
                    int r = ThreadLocalRandom.current().nextInt(0, seats);// excludes
                                                                            // seats
                                                                            //
                    AtomicReference<Integer> el = l.get(r);
                    reserved = el.compareAndSet(null, id);// null means no user
                                                            // has reserved this
                                                            // seat
                    if (reserved)
                        full.getAndIncrement();
                }
                if (!reserved && full.get() == seats)
                    System.out.println("user " + id + " did not get a seat");
            } catch (InterruptedException ie) {
                // log it
            }
        }
    }

}    

다른 간단한 예로는 Session 개체에서 세이프 스레드 수정을 수행하는 방법이 있습니다.

public PlayerScore getHighScore() {
    ServletContext ctx = getServletConfig().getServletContext();
    AtomicReference<PlayerScore> holder 
        = (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
    return holder.get();
}

public void updateHighScore(PlayerScore newScore) {
    ServletContext ctx = getServletConfig().getServletContext();
    AtomicReference<PlayerScore> holder 
        = (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
    while (true) {
        HighScore old = holder.get();
        if (old.score >= newScore.score)
            break;
        else if (holder.compareAndSet(old, newScore))
            break;
    } 
}

출처 : http://www.ibm.com/developerworks/library/j-jtp09238/index.html

언급URL : https://stackoverflow.com/questions/3964211/when-to-use-atomicreference-in-java

반응형