เมื่อใดที่ใช้ AtomicReference ใน Java


311

เราจะใช้เมื่อไหร่AtomicReference?

จำเป็นต้องสร้างวัตถุในโปรแกรมแบบมัลติเธรดทั้งหมดหรือไม่?

ระบุตัวอย่างง่ายๆที่ควรใช้ AtomicReference

คำตอบ:


215

ควรใช้การอ้างอิงแบบอะตอมมิกในการตั้งค่าที่คุณต้องการดำเนินการอย่างง่ายแบบอะตอมมิค (เช่นthread-safe , non-trivial) ในการอ้างอิงซึ่งการซิงโครไนซ์บนจอมอนิเตอร์ไม่เหมาะสม สมมติว่าคุณต้องการตรวจสอบเพื่อดูว่าเขตข้อมูลเฉพาะเฉพาะถ้าสถานะของวัตถุยังคงอยู่ในขณะที่คุณตรวจสอบล่าสุด:

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โดยทั่วไปคุณควรใช้ซิงโครไนซ์หรือjava.util.concurrentเฟรมเวิร์กแทนที่จะดีกว่าAtomic*เว้นแต่ว่าคุณรู้ว่ากำลังทำอะไรอยู่

การอ้างอิงต้นไม้ต้นตายที่ยอดเยี่ยมสองประการซึ่งจะแนะนำคุณในหัวข้อนี้:

โปรดทราบว่า (ฉันไม่รู้ว่าสิ่งนี้เป็นจริงเสมอ) การอ้างอิงอ้างอิง (เช่น=) เป็นตัวอะตอม (อัปเดตชนิด 64 บิตดั้งเดิมเช่นlongหรือdoubleอาจไม่ใช่อะตอม แต่การอัปเดตการอ้างอิงเป็นอะตอมเสมอแม้ว่าจะเป็น 64 บิต ) Atomic*โดยไม่ต้องใช้อย่างชัดเจน
ดู3ed Java จำเพาะภาษา, มาตรา 17.7


43
แก้ไขให้ถูกต้องหากฉันผิด แต่ดูเหมือนว่ากุญแจที่จะต้องใช้สิ่งนี้คือเพราะคุณต้องทำ "comparAndSet" ถ้าสิ่งที่ฉันต้องทำคือตั้งค่าฉันไม่ต้องการ AtomicObject เลยเพราะการอ้างอิงการปรับปรุงตัวเองเป็นอะตอมมิก
sMoZely

ปลอดภัยไหมที่จะทำ cache.compareAndSet (cachedValue, someFunctionOfOld (cachedValueToUpdate)) เช่นการคำนวณแบบอินไลน์หรือไม่
kaqqao

4
@veggen ฟังก์ชั่นข้อโต้แย้งใน Java ได้รับการประเมินก่อนที่ฟังก์ชั่นของตัวเองดังนั้นการ inlining ทำให้ไม่มีความแตกต่างในกรณีนี้ ใช่มันปลอดภัย
มิทรี

29
@sMoZely นั้นถูกต้อง แต่ถ้าคุณไม่ได้ใช้AtomicReferenceคุณควรทำเครื่องหมายตัวแปรvolatileเพราะในขณะที่รันไทม์รับประกันว่าการมอบหมายการอ้างอิงคืออะตอมคอมไพเลอร์อาจดำเนินการปรับให้เหมาะสมภายใต้สมมติฐานว่าตัวแปรไม่ได้ถูกแก้ไขโดยเธรดอื่น
kbolino

1
@BradCupit ทราบว่าฉันพูดว่า "ถ้าคุณไม่ได้ใช้AtomicReference"; หากคุณกำลังใช้งานอยู่คำแนะนำของฉันก็คือไปในทิศทางตรงกันข้ามและทำเครื่องหมายfinalเพื่อให้คอมไพเลอร์สามารถปรับให้เหมาะสม
kbolino

91

การอ้างอิงอะตอมมิกนั้นเหมาะที่จะใช้เมื่อคุณต้องการแบ่งใช้และเปลี่ยนสถานะของวัตถุที่ไม่เปลี่ยนรูปแบบระหว่างหลายเธรด นั่นเป็นคำแถลงที่มีความหนาแน่นสูงมากดังนั้นฉันจะทำลายมันลงเล็กน้อย

ก่อนวัตถุที่ไม่เปลี่ยนรูปเป็นวัตถุที่ไม่เปลี่ยนแปลงอย่างมีประสิทธิภาพหลังการก่อสร้าง บ่อยครั้งที่เมธอดของวัตถุที่ไม่เปลี่ยนรูปแบบจะส่งคืนอินสแตนซ์ใหม่ของคลาสเดียวกันนั้น บางตัวอย่างรวมคลาส wrapper ของ Long and Double และ String เพียงเพื่อตั้งชื่อให้สองสามตัว (ตามการเขียนโปรแกรมพร้อมกันใน JVMวัตถุที่ไม่เปลี่ยนรูปแบบเป็นส่วนสำคัญของการเกิดพร้อมกันที่ทันสมัย)

ถัดไปทำไม AtomicReference ดีกว่าวัตถุที่ระเหยได้สำหรับการแบ่งปันค่าที่ใช้ร่วมกัน ตัวอย่างโค้ดแบบง่ายจะแสดงความแตกต่าง

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

ทุกครั้งที่คุณต้องการแก้ไขสตริงที่อ้างอิงโดยเขตข้อมูลระเหยนั้นตามค่าปัจจุบันคุณต้องได้รับการล็อคบนวัตถุนั้นก่อน สิ่งนี้จะป้องกันไม่ให้เธรดอื่นเข้ามาในระหว่างนี้และเปลี่ยนค่าที่อยู่ตรงกลางของการต่อสตริงใหม่ จากนั้นเมื่อเธรดของคุณดำเนินการต่อคุณปิดกั้นการทำงานของเธรดอื่น แต่จริงๆแล้วรหัสนั้นจะใช้งานได้ดูสะอาดตาและจะทำให้คนส่วนใหญ่มีความสุข

ปัญหาเล็กน้อย มันช้า โดยเฉพาะอย่างยิ่งหากมีการโต้แย้งจำนวนมากของวัตถุล็อคนั้น นั่นเป็นเพราะการล็อคส่วนใหญ่ต้องการการเรียกใช้ระบบปฏิบัติการและเธรดของคุณจะบล็อกและเปลี่ยนบริบทจาก CPU เพื่อหลีกทางให้กระบวนการอื่น ๆ

ตัวเลือกอื่นคือใช้ AtomicRefrence

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);
}

ตอนนี้ทำไมถึงดีกว่า จริงๆแล้วรหัสนั้นสะอาดน้อยกว่าเดิมเล็กน้อย แต่มีบางสิ่งที่สำคัญจริงๆที่เกิดขึ้นภายใต้ประทุนใน AtomicRefrence และนั่นคือการเปรียบเทียบและสลับ มันเป็นคำสั่ง CPU เดียวไม่ใช่การเรียกใช้ระบบปฏิบัติการที่ทำให้สวิตช์เกิดขึ้น นั่นเป็นคำสั่งเดียวของ CPU และเนื่องจากไม่มีการล็อคจึงไม่มีการสลับบริบทในกรณีที่การล็อคถูกใช้ซึ่งช่วยประหยัดเวลาได้มากขึ้น!

การดักจับคือสำหรับ AtomicReferences สิ่งนี้ไม่ได้ใช้การเรียก. Equals () แต่เป็นการเปรียบเทียบ == สำหรับค่าที่คาดไว้ ดังนั้นตรวจสอบให้แน่ใจว่าสิ่งที่คาดหวังคือวัตถุจริงที่ส่งคืนมาจากรับในลูป


14
ตัวอย่างทั้งสองของคุณทำงานแตกต่างกัน คุณต้องวนซ้ำworkedเพื่อให้ได้ความหมายเดียวกัน
CurtainDog

5
ฉันคิดว่าคุณควรเริ่มต้นค่าภายในตัวสร้าง AtomicReference มิฉะนั้นเธรดอื่นอาจยังเห็นค่า null ก่อนที่คุณจะเรียกใช้ shared.set (ยกเว้น shared.set มีการเรียกใช้ในการเริ่มต้นคง.)
Henno Vermeulen

8
ในตัวอย่างที่สองของคุณคุณควรใช้ Java 8 ใช้ดังนี้: shared.updateAndGet ((x) -> (x + "ให้เพิ่มบางอย่าง")); ... ซึ่งจะเรียก. compareAndSet ซ้ำ ๆ จนกว่ามันจะทำงาน นั่นเท่ากับบล็อกที่ซิงโครไนซ์ซึ่งจะประสบความสำเร็จเสมอ คุณต้องตรวจสอบให้แน่ใจว่าแลมบ์ดาที่คุณส่งผ่านนั้นไม่มีผลข้างเคียงแม้ว่าจะถูกเรียกหลายครั้ง
Tom Dibble

2
ไม่จำเป็นต้องทำการทำให้ String ที่ใช้ร่วมกันระเหยได้ การซิงโครไนซ์ (ล็อก) ดีพอที่จะสร้างสิ่งที่เกิดขึ้นก่อนความสัมพันธ์
ใจ Pandit

2
"... เปลี่ยนสถานะของวัตถุที่เปลี่ยนไม่ได้" นั้นไม่ชัดเจนที่นี่ส่วนหนึ่งเป็นเพราะตัวอักษรคุณไม่สามารถเปลี่ยนสถานะของวัตถุที่ไม่เปลี่ยนรูปได้ ตัวอย่างแสดงการเปลี่ยนการอ้างอิงจากอินสแตนซ์วัตถุที่ไม่เปลี่ยนรูปแบบหนึ่งไปเป็นอีกแบบหนึ่ง ฉันรู้ว่ามันเชื่องช้า แต่ฉันคิดว่ามันคุ้มค่าที่จะให้ความสำคัญเนื่องจากความสับสนของ Thread Thread สามารถทำได้
Mark Phillips

30

นี่คือกรณีการใช้งานสำหรับ AtomicReference:

พิจารณาคลาสนี้ที่ทำหน้าที่เป็นช่วงตัวเลขและใช้ตัวแปร 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) และหนึ่งเธรดเรียก setLower (5) ในขณะที่เธรดอื่นเรียก setUpper (4) ด้วยช่วงเวลาที่โชคไม่ดีทั้งคู่จะผ่านการตรวจสอบในตัวตั้งค่าและการแก้ไขทั้งสองจะถูกนำไปใช้ ผลลัพธ์คือช่วงนี้มีสถานะไม่ถูกต้อง (5, 4) ดังนั้นในขณะที่ AtomicIntegers ที่อยู่ภายใต้เธรดปลอดภัยคลาสคอมโพสิตไม่ สามารถแก้ไขได้โดยใช้ AtomicReference แทนการใช้ AtomicIntegers แต่ละรายการสำหรับขอบเขตบนและล่าง

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;
        }
    }
}

2
บทความนี้คล้ายกับคำตอบของคุณ แต่จะลึกลงไปถึงสิ่งที่ซับซ้อนกว่า มันน่าสนใจ! ibm.com/developerworks/java/library/j-jtp04186
LppEdd

20

คุณสามารถใช้ AtomicReference เมื่อใช้การล็อคในแง่ดี คุณมีวัตถุที่ใช้ร่วมกันและคุณต้องการเปลี่ยนจากเธรดมากกว่า 1 รายการ

  1. คุณสามารถสร้างสำเนาของวัตถุที่ใช้ร่วมกัน
  2. ปรับเปลี่ยนวัตถุที่ใช้ร่วมกัน
  3. คุณต้องตรวจสอบว่าวัตถุที่ใช้ร่วมกันยังคงเหมือนเดิม - ถ้าใช่แล้วอัปเดตโดยอ้างอิงจากสำเนาที่แก้ไข

เนื่องจากเธรดอื่นอาจแก้ไขและ / สามารถแก้ไขระหว่าง 2 ขั้นตอนเหล่านี้ คุณต้องทำมันในการปฏิบัติการปรมาณู นี่คือสิ่งที่ AtomicReference สามารถช่วยได้


7

นี่คือกรณีการใช้งานที่ง่ายมากและไม่เกี่ยวข้องกับความปลอดภัยของเธรด

ในการแชร์วัตถุระหว่างการร้องขอแลมบ์ดาAtomicReferenceตัวเลือกคือ:

public void doSomethingUsingLambdas() {

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

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

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

ฉันไม่ได้บอกว่านี่คือการออกแบบที่ดีหรืออะไรก็ตาม (มันเป็นเพียงตัวอย่างเล็กน้อย) แต่ถ้าคุณมีกรณีที่คุณต้องการแชร์วัตถุระหว่างการขอแลมบ์ดา AtomicReferenceนั่นเป็นตัวเลือก

ในความเป็นจริงคุณสามารถใช้วัตถุใด ๆ ที่เก็บข้อมูลอ้างอิงแม้กระทั่งชุดรวมที่มีเพียงรายการเดียว อย่างไรก็ตาม AtomicReference นั้นเหมาะสมอย่างยิ่ง


6

ฉันจะไม่พูดอะไรมาก เพื่อนเพื่อนที่เคารพนับถือของฉันได้ให้ความเห็นที่มีค่ารหัสที่ใช้งานได้อย่างสมบูรณ์ในตอนท้ายของบล็อกนี้ควรลบความสับสนใด ๆ มันเกี่ยวกับการจองที่นั่งภาพยนตร์ขนาดเล็กในสถานการณ์แบบมัลติเธรด

ข้อเท็จจริงเบื้องต้นที่สำคัญบางประการมีดังนี้ 1> เธรดที่แตกต่างกันสามารถแข่งขันได้กับอินสแตนซ์และตัวแปรสมาชิกแบบคงที่ในพื้นที่ฮีพ 2> การอ่านหรือเขียนแบบระเหยนั้นมีทั้งอะตอมและต่อเนื่อง / เกิดขึ้นก่อนและจากหน่วยความจำเท่านั้น ด้วยการพูดแบบนี้ฉันหมายความว่าการอ่านใด ๆ จะเป็นไปตามการเขียนในหน่วยความจำก่อนหน้า และการเขียนใด ๆ จะติดตามอ่านจากหน่วยความจำก่อนหน้า ดังนั้นเธรดใด ๆ ที่ทำงานกับสารระเหยจะเห็นค่าที่ทันสมัยที่สุดเสมอ AtomicReference ใช้คุณสมบัติของสารระเหย

ต่อไปนี้เป็นซอร์สโค้ดบางส่วนของ AtomicReference AtomicReference อ้างถึงการอ้างอิงวัตถุ การอ้างอิงนี้เป็นตัวแปรสมาชิกระเหยในอินสแตนซ์ AtomicReference ดังต่อไปนี้

private volatile V value;

รับ () เพียงแค่ส่งกลับค่าล่าสุดของตัวแปร (เช่น volatiles ทำในลักษณะ "เกิดขึ้นก่อน")

public final V get()

ต่อไปนี้เป็นวิธีที่สำคัญที่สุดของ AtomicReference

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

เมธอด comparAndSet (expect, update) เรียกเมธอด comparAndSwapObject () ของคลาสที่ไม่ปลอดภัยของ Java การเรียกเมธอดที่ไม่ปลอดภัยนี้เป็นการเรียกใช้การเรียกดั้งเดิมซึ่งจะเรียกใช้คำสั่งเดียวกับตัวประมวลผล "คาดหวัง" และ "อัปเดต" อ้างอิงแต่ละวัตถุ

หากและถ้าหากสมาชิกอินสแตนซ์ของตัวแปร AtomicReference "ค่า" อ้างอิงถึงวัตถุเดียวกันนั้นถูกอ้างถึงโดย "expect", "update" จะถูกกำหนดให้กับตัวแปรอินสแตนซ์นี้ทันทีและจะส่งคืน "true" มิฉะนั้นจะส่งคืน false สิ่งทั้งหมดทำแบบอะตอม ไม่มีเธรดอื่นใดสามารถดักจับได้ เนื่องจากนี่เป็นการดำเนินการตัวประมวลผลเดียว (ความมหัศจรรย์ของสถาปัตยกรรมคอมพิวเตอร์สมัยใหม่) จึงมักจะเร็วกว่าการใช้บล็อกแบบซิงโครไนซ์ แต่โปรดจำไว้ว่าเมื่อจำเป็นต้องอัปเดตตัวแปรหลายตัวตามปกติ AtomicReference จะไม่ช่วยได้

ฉันต้องการเพิ่มรหัสการรันแบบเต็มซึ่งสามารถเรียกใช้ใน eclipse มันจะขจัดความสับสนมากมาย ที่นี่ผู้ใช้ 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
            }
        }
    }

}    

5

เราจะใช้ AtomicReference เมื่อใด

AtomicReferenceเป็นวิธีที่ยืดหยุ่นในการอัปเดตค่าตัวแปรแบบอะตอมมิกโดยไม่ต้องใช้การซิงโครไนซ์

AtomicReference รองรับการตั้งโปรแกรมล็อคเธรดที่ไม่มีล็อคในตัวแปรเดี่ยว

มีหลายวิธีในการบรรลุความปลอดภัยของเธรดด้วยAPI พร้อมกันในระดับสูง ตัวแปรอะตอมเป็นหนึ่งในหลายตัวเลือก

Lock วัตถุที่รองรับการล็อคสำนวนที่ทำให้การใช้งานพร้อมกันจำนวนมากง่ายขึ้น

Executorsกำหนด API ระดับสูงสำหรับการเรียกใช้และการจัดการเธรด การใช้งาน Executor ที่จัดทำโดย 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/ synchronizedฯลฯ


-1

อีกตัวอย่างง่าย ๆ คือทำการปรับเปลี่ยนเธรดที่ปลอดภัยในวัตถุเซสชัน

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.