มีรายการพร้อมกันใน JDK ของ Java หรือไม่


277

ฉันจะสร้างอินสแตนซ์ของรายการที่เกิดขึ้นพร้อมกันซึ่งฉันสามารถเข้าถึงองค์ประกอบตามดัชนีได้อย่างไร JDK มีคลาสหรือวิธีการจากโรงงานใดบ้างที่ฉันสามารถใช้ได้


28
ทำไมไม่สร้างสรรค์? CopyOnWriteArrayList ที่เสนอหลายรายการที่ไม่พบใน. Net คุณสามารถพูดได้ว่าคำถามทั้งสองเกี่ยวข้องกัน แต่ไม่ปิด !!!
AlikElzin-kilaka

1
ฉันไม่มีความคิดว่าทำไม Jarrod Roberson ถึงคิดว่าเป็นความคิดที่ดีที่จะแก้ไขรายละเอียดที่ทำโดยสเตฟานและเปลี่ยนกลับไปใช้คำถามเดิมที่มีประโยคไม่ดี คำตอบของ Jarrod ยังคงเป็นที่ยอมรับอย่างสมบูรณ์ ในความเป็นจริง CopyOnWriteArrayList เป็นคลาสที่เกิดขึ้นพร้อมกันเท่านั้นที่ใช้ List ใน JDK งงงวย ...
Matt Passell

10
เพราะคำตอบที่ได้รับการยอมรับเป็นไปคำถามเดิมและสเตฟานใส่เป็นคำถามที่ไม่เกี่ยวข้องกับพวงของรหัสที่มาที่เดิมโปสเตอร์ไม่ได้รวมถึงทุกที่เปลี่ยนคำถามที่สมบูรณ์ซึ่งสร้างคำตอบเพิ่มเติมที่ได้รับการแนะนำสิ่งอื่น ๆ นอกเหนือจากListที่เดิมโดยเฉพาะ กล่าวว่าเป็นข้อกำหนดที่ถือเป็นการก่อกวน ผู้ดำเนินรายการล็อคคำถามไว้แล้วเนื่องจากคนที่บ่นว่าคำตอบนั้นไม่ได้ตอบคำถามรุ่นนั้นที่ถูกทำลาย

1
/ locked/ closed/ ความคิดเห็นก่อนหน้า

8
ไม่มีเหตุผลที่จะปิดคำถามนี้ มันถามเกี่ยวกับชั้นเรียนใน JDK ซึ่งไม่เหมือนกับการค้นหาห้องสมุด มันเป็นฐานของ Java
maaartinus

คำตอบ:


175

มีการดำเนินงานที่เกิดขึ้นพร้อมกันในรายการเป็นjava.util.concurrent โดยเฉพาะอย่างยิ่งCopyOnWriteArrayList


84
โปรดทราบว่ามันจะคัดลอกรายการทั้งหมดในทุกส่วนแทรกดังนั้นจึงมักไม่มีประสิทธิภาพ
dfrankow

22
@frankow แต่มันจะมีประสิทธิภาพมากขึ้นถ้าคุณทำซ้ำมากกว่าที่คุณกำลังอัพเดท
b1nary.atr0phy

ทำงานได้ไม่ดีดังที่แสดงไว้ที่นี่ ฉันมีข้อยกเว้นแม้ว่าฉันจะใช้วิธีการของ addAll และอ่านมันโดยใช้สตรีม stackoverflow.com/questions/1527519/…
devssh

166

หากคุณไม่สนใจเกี่ยวกับที่มีการเข้าถึงดัชนี-based และต้องการเพียงแค่ลักษณะการแทรกคำสั่งรักษาของรายการคุณสามารถพิจารณาjava.util.concurrent.ConcurrentLinkedQueue เนื่องจากมันใช้ Iterable ได้เมื่อคุณเพิ่มรายการทั้งหมดเสร็จแล้วคุณสามารถวนซ้ำเนื้อหาโดยใช้การปรับปรุงสำหรับไวยากรณ์:

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}

4
ฉันคิดว่าคำสั่งเรียบง่ายสำหรับ ( :) เรียกว่า foreach: docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka

2
@ AlikElzin-kilaka คุณพูดถูก ฉันคิดว่าชื่อนั้นรบกวนฉันเสมอเพราะไวยากรณ์ที่แท้จริงไม่ได้รวมคำว่า "แต่ละคน" แต่ฉันจะอัปเดตคำตอบเพื่อใช้ชื่ออย่างเป็นทางการ :)
Matt Passell

3
@ AlikElzin-kilaka Nitpicking แต่ตามJLS เวอร์ชัน 8เรียกว่า "Enhanced for statement" เช่นเดียวกันในการสอนด้วยจาวา
Roland

1
@Roland แน่นอนไม่ nitpicking มี (ตอนนี้) ความแตกต่างระหว่าง "สำหรับแต่ละ" และ "ปรับปรุงสำหรับ" ใน Java
hfontanez

2
@Roland ทางอ้อม ฉันเชื่อว่าพวกเขาเปลี่ยนชื่อ "สำหรับแต่ละลูป" เป็น "ปรับปรุงสำหรับ" เพื่อกำจัดความสับสนระหว่างสตรีม. forEach และตอนนี้สิ่งที่เป็นที่รู้จักกันดีสำหรับ
hfontanez

128

คุณสามารถใช้Collections.synchronizedList (รายการ) ได้เป็นอย่างดีหากคุณต้องการเพียงแค่การซิงโครไนซ์การเรียกใช้:

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

70
ผลลัพธ์ของการsynchronizedList"ตรงกัน" แต่ไม่ใช่ "พร้อมกัน" ปัญหาพื้นฐานหนึ่งที่การดำเนินการ List หลายรายการซึ่งเป็นแบบอิงดัชนีไม่ได้เป็นแบบอะตอมมิกและจำเป็นต้องเป็นส่วนหนึ่งของโครงสร้างการแยกซึ่งกันและกันที่ใหญ่กว่า

6
IMO, asing ตรงไปตรงมามากกว่าVector Collections.synchronizedList(new ArrayList<Object>())
เตฟาน

8
ผลลัพธ์ของ synchronList คือ "ทำข้อมูลให้ตรงกัน" แต่ไม่ใช่ "ทำงานพร้อมกัน"
Kanagavelu Sugumar

46

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

แนวคิดที่สำคัญของคอลเลกชันที่เกิดขึ้นพร้อมกันคือการดำเนินการแต่ละอย่างของตัวเองคืออะตอมมิกและสามารถทำได้โดยไม่ต้องมีการล็อค / การซิงโครไนซ์อย่างชัดเจน

ดังนั้นการทำให้องค์ประกอบอยู่ในตำแหน่งที่กำหนดnจากListการดำเนินการของอะตอมจึงไม่สมเหตุสมผลในสถานการณ์ที่คาดว่าจะมีการเข้าถึงพร้อมกัน


6
โจอาคิมฉันคิดว่าคุณโดนเล็บบนหัว ยกตัวอย่างเช่นรายการอ่านอย่างเดียวเป็นรายการที่เกิดขึ้นพร้อมกัน รับองค์ประกอบที่ตำแหน่ง N จากรายการไม่เพียง แต่ทำให้รู้สึก แต่มันสั้นของปัญหา ดังนั้นรายการที่ไม่เปลี่ยนรูป (ตัวพิมพ์เล็ก L) จะเป็นตัวอย่างที่ดี แต่ไม่ใช่รายการ (ตัวพิมพ์ใหญ่ L) CopyOnWriteArrayList พร้อมกัน แต่หลายคนไม่ชอบประสิทธิภาพ การแก้ปัญหาตามสายเชือก (string ropes) อาจเป็นผู้ชนะที่ดี
johnstosh

1
จุดที่ดีมาก แต่รายการ OP ที่กำลังใช้อยู่อาจมีการใช้งานที่เฉพาะเจาะจงมาก เช่นมันอาจจะเต็มไปด้วยสภาพแวดล้อมพร้อมกันแล้ว "ล็อค" (สิ่งที่มันหมายถึง) แล้วเข้าถึงได้อย่างปลอดภัยโดยดัชนี ดังนั้นในระยะแรกของการกรอกรายการดังกล่าวจะยังคงต้องมีการใช้งานเธรดที่ปลอดภัย น่าเสียดายที่ OP ไม่ได้เจาะจงเกี่ยวกับการใช้รายการที่เขากำลังมองหา
igor.zh

13

คุณมีตัวเลือกเหล่านี้:

  • Collections.synchronizedList()คุณสามารถห่อใด ๆListการดำเนินงาน ( ArrayList, LinkedListหรือรายชื่อของบุคคลที่ 3 ก) เข้าถึงทุกวิธี (การอ่านและการเขียน) synchronizedจะได้รับการคุ้มครองโดยใช้ เมื่อใช้iterator()หรือปรับปรุงสำหรับลูปคุณต้องซิงโครไนซ์ด้วยตนเอง ในขณะที่วนซ้ำเธรดอื่น ๆ จะถูกบล็อกอย่างสมบูรณ์แม้กระทั่งการอ่าน คุณยังสามารถซิงโครไนซ์แยกกันสำหรับการโทรแต่ละครั้งhasNextและnextโทรออกได้ แต่ก็ConcurrentModificationExceptionเป็นไปได้

  • CopyOnWriteArrayList: การแก้ไขมีราคาแพง แต่ไม่ต้องรอให้อ่าน Iterators ไม่เคยโยนConcurrentModificationExceptionพวกเขาคืนค่าสแนปชอตของรายการในขณะที่สร้างตัววนซ้ำแม้ว่ารายการนั้นจะถูกแก้ไขโดยเธรดอื่นในขณะที่วนซ้ำ มีประโยชน์สำหรับรายการที่อัปเดตไม่บ่อยครั้ง ต้องการการดำเนินการแบบกลุ่มเช่นaddAllอัปเดต - อาร์เรย์ภายในจะถูกคัดลอกน้อยลงหลายครั้ง

  • Vector: ชอบมากsynchronizedListแต่การทำซ้ำก็ทำข้อมูลให้ตรงกันด้วย อย่างไรก็ตามตัววนซ้ำสามารถโยนConcurrentModificationExceptionได้หากเวกเตอร์ถูกแก้ไขโดยเธรดอื่นในขณะที่วนซ้ำ

ตัวเลือกอื่น:

  • Collections.unmodifiableList(): ปลอดล็อคเกลียวปลอดภัย แต่ไม่สามารถแก้ไขได้
  • QueueหรือDequeอาจเป็นทางเลือกถ้าคุณเพียงเพิ่ม / ลบที่ท้ายรายการและวนซ้ำรายการ ไม่มีการเข้าถึงโดยดัชนีและไม่มีการเพิ่ม / ลบในตำแหน่งที่ต้องการ พวกเขามีการใช้งานพร้อมกันหลายครั้งที่มีประสิทธิภาพที่ดีขึ้นและการเข้าถึงพร้อมกันที่ดีกว่า แต่มันเกินขอบเขตของคำถามนี้ คุณยังสามารถดูJCToolsซึ่งมีการใช้คิวที่มีประสิทธิภาพมากขึ้นสำหรับผู้บริโภครายเดียวหรือผู้ผลิตรายเดียว

4

ป้อนคำอธิบายรูปภาพที่นี่

CopyOnWriteArrayList เป็นตัวแปรชุดเธรดที่ปลอดภัยของ ArrayList ซึ่งการดำเนินการเปลี่ยนแปลงทั้งหมด (เพิ่มตั้งค่าและอื่น ๆ ) ถูกนำไปใช้โดยการสร้างสำเนาใหม่ของอาร์เรย์ต้นแบบ

CopyOnWriteArrayList เป็นทางเลือกที่เกิดขึ้นพร้อมกันของการทำข้อมูลให้ตรงกันโดยใช้ List List Interface และเป็นส่วนหนึ่งของ java.util.concurrent package และชุดของเธรดที่ปลอดภัย

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList ไม่ปลอดภัยและไม่ทิ้ง ConcurrentModificationException เมื่อต้นแบบ CopyOnWriteArrayList พื้นฐานได้รับการแก้ไขในระหว่างการทำซ้ำใช้ ArrayList แยกต่างหาก

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

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.