Java Concurrent Hashmap initTable () ทำไมต้องลอง / บล็อกในที่สุด

Aug 29 2020

ฉันได้ดูรหัสต่อไปนี้ (มีที่มาจากที่นี่ ) '

/**
 * Initializes table, using the size recorded in sizeCtl.
 */
private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    while ((tab = table) == null || tab.length == 0) {
        if ((sc = sizeCtl) < 0)
            Thread.yield(); // lost initialization race; just spin
        else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
            try {
                if ((tab = table) == null || tab.length == 0) {
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                    @SuppressWarnings("unchecked")
                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                    table = tab = nt;
                    sc = n - (n >>> 2);
                }
            } finally {
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}

มีใครสามารถอธิบายได้ว่าทำไมจึงต้องมีการลองบล็อก

คำตอบ

3 rzwitserloot Aug 29 2020 at 20:31

มีข้อยกเว้นและข้อผิดพลาดที่ไม่ได้ตรวจสอบเป็นแนวคิด หากคุณเรียก.put()ใช้ ConcurrentHashMap และคุณมีหน่วยความจำแน่นจริงแผนที่นั้นอาจพยายามสร้างอาร์เรย์และการเรียกนั้นอาจล้มเหลวด้วย OutOfMemoryError รหัสจะยังคงดำเนินต่อไป (ที่บล็อกการจับที่จับสิ่งนี้ได้) และการอ้างอิงถึงแผนที่นั้นยังคงมีอยู่ มันจะค่อนข้างแย่ถ้าหลังจากนั้นเกิดขึ้นแผนที่ขัดข้องและไม่ถูกต้องอย่างสมบูรณ์เนื่องจาก sizeCtl มีค่าเสีย

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

ความเกี่ยวข้องของสิ่งนี้ (เช่นเดียวกับที่เกิดขึ้นที่นี่บ่อยเพียงใดเมื่อเทียบกับการลบ 'ลอง' และ 'ในที่สุด' และลงท้ายด้วยsizeCtl = sc;โดยไม่ต้องมีค่าใช้จ่ายเพิ่มเติมในการลอง / ในที่สุด) นั้นต่ำ แต่ถ้ามีความเกี่ยวข้อง มันค่อนข้างเกี่ยวข้อง

Den-Jason Aug 29 2020 at 20:32

มันคือ mutex ชนิดหนึ่งและตรวจสอบการล็อกอีกครั้ง

ประการแรก

if ((sc = sizeCtl) < 0)
    Thread.yield(); // lost initialization race; just spin

ตรวจสอบsizeCtlmutex ถ้ามันน้อยกว่าศูนย์แสดงว่ามีคนอื่นคว้าไปได้ดังนั้นเราก็รอสักครู่

หากเป็นศูนย์ (หรือมากกว่า) โอกาสที่เราจะได้รับการล็อค ดังนั้นการเปรียบเทียบและสลับจึงดำเนินการ:

if (U.compareAndSetInt(this, SIZECTL, sc, -1)

นี่คือการทำงานของอะตอม - หากเธรดอื่นจับมันระหว่างการตรวจสอบครั้งแรกและครั้งที่สองifจะไม่ถูกนำมาใช้

หาก CAS ประสบความสำเร็จวิธีนี้จะต้องรับผิดชอบในการปล่อย mutex ดังนั้น a try-finallyจึงถูกใช้เพื่อให้แน่ใจว่า mutex ถูกปล่อยออกมา ( sizeCtlรีเซ็ตเป็นค่าเดิม) โดยไม่คำนึงว่าจะมีข้อยกเว้นเกิดขึ้น (เช่นหน่วยความจำไม่เพียงพอในการกำหนด Node ใหม่ - หรือมีข้อ จำกัด เฉพาะที่เกี่ยวข้องกับแผนที่) หรือไม่