วิธีใช้ ConcurrentLinkedQueue


97

ฉันจะใช้ a ConcurrentLinkedQueueใน Java ได้อย่างไร
เมื่อใช้สิ่งนี้LinkedQueueฉันต้องกังวลเกี่ยวกับการทำงานพร้อมกันในคิวหรือไม่ หรือฉันต้องกำหนดสองวิธี (วิธีหนึ่งเพื่อดึงองค์ประกอบจากรายการและอีกวิธีหนึ่งเพื่อเพิ่มองค์ประกอบในรายการ)
หมายเหตุ: ต้องมีการซิงโครไนซ์สองวิธีนี้อย่างชัดเจน ขวา?


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

อีกคำถามหนึ่ง: ฉันต้องทำสิ่งนี้ในวิธีลบ:

while (queue.size() == 0){ 
  wait(); 
  queue.poll();
}

ฉันมีผู้บริโภคเพียงรายเดียวและผู้ผลิตรายเดียว


ขอบคุณสำหรับคำตอบสำหรับคำถาม mt สิ่งที่ฉันพยายามทำคือ: ฉันมีคลาส (ใน Java) ที่มีวิธีการหนึ่งในการดึงข้อมูลจากคิวและอีกคลาสหนึ่งด้วยวิธีการหนึ่งในการเพิ่มรายการลงในคิว รายการที่เพิ่มและดึงมาจากรายการเป็นวัตถุของคลาสของฉันเอง
Ricardo Felgueiras

2
คุณควรแก้ไขคำถามของคุณและใส่คำชี้แจงนี้ไว้ในคำถามนั้นเอง
Adam Jaskiewicz

คำตอบ:


159

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

ขั้นแรกสร้างคิวของคุณ:

Queue<YourObject> queue = new ConcurrentLinkedQueue<YourObject>();

ตอนนี้ไม่ว่าคุณจะสร้างวัตถุผู้ผลิต / ผู้บริโภคของคุณที่ใดก็ตามให้ส่งต่อในคิวเพื่อให้พวกเขามีที่วางวัตถุ (คุณสามารถใช้ตัวตั้งค่าสำหรับสิ่งนี้แทนได้ แต่ฉันชอบทำสิ่งนี้ในตัวสร้าง):

YourProducer producer = new YourProducer(queue);

และ:

YourConsumer consumer = new YourConsumer(queue);

และเพิ่มเนื้อหาในโปรดิวเซอร์ของคุณ:

queue.offer(myObject);

และนำสิ่งต่างๆออกไปในผู้บริโภคของคุณ (หากคิวว่างเปล่าแบบสำรวจ () จะส่งคืนค่าว่างดังนั้นให้ตรวจสอบ):

YourObject myObject = queue.poll();

สำหรับข้อมูลเพิ่มเติมโปรดดูJavadoc

แก้ไข:

หากคุณต้องการบล็อกการรอให้คิวไม่ว่างคุณอาจต้องการใช้LinkedBlockingQueueและใช้เมธอด take () อย่างไรก็ตาม LinkedBlockingQueue มีความจุสูงสุด (ค่าเริ่มต้นคือ Integer.MAX_VALUE ซึ่งมากกว่าสองพันล้าน) ดังนั้นจึงอาจเหมาะสมหรือไม่ก็ได้ขึ้นอยู่กับสถานการณ์ของคุณ

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

Queue<YourObject> queue = Collections.synchronizedList(new LinkedList<YourObject>());

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

สุดท้าย:

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

BlockingQueue<YourObject> queue = new LinkedBlockingQueue<YourObject>();

queue.put(myObject); // Blocks until queue isn't full.

YourObject myObject = queue.take(); // Blocks until queue isn't empty.

อย่างอื่นเหมือนกันหมด Put อาจไม่บล็อกเพราะคุณไม่น่าจะใส่วัตถุสองพันล้านชิ้นลงในคิว


ขอบคุณสำหรับคำตอบของคุณ อีกคำถามหนึ่ง: ฉันต้องทำสิ่งนี้ในวิธีการลบ: while (que.size () == 0) wait (); que.poll ();
Ricardo Felgueiras

ฉันจะตอบว่าเป็นการแก้ไขคำตอบของฉันเนื่องจากมันสำคัญมาก
Adam Jaskiewicz

ในฐานะที่ทำให้เกิดความสับสนในคำถามอื่นCollection.synchronizedListส่งกลับที่ไม่ได้ดำเนินการList Queue
Tom Hawtin - แท็กไลน์

@AdamJaskiewicz ใช้ConcurrentLinkedQueueสำหรับ Producer Consumer เป็นความคิดที่ดีฉันหมายถึงโพสต์นี้stackoverflow.com/questions/1426754/…
rd22

37

นี้เป็นส่วนใหญ่ซ้ำคำถามอื่น

นี่คือส่วนของคำตอบที่เกี่ยวข้องกับคำถามนี้:

ฉันจำเป็นต้องทำการซิงโครไนซ์ด้วยตัวเองหรือไม่ถ้าฉันใช้ java.util.ConcurrentLinkedQueue

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

ตัวอย่างเช่นนี่คือเธรดที่ปลอดภัยโดยไม่มีการดำเนินการใด ๆ ในส่วนของคุณ:

queue.add(obj);

หรือ

queue.poll(obj);

อย่างไรก็ตาม; การเรียกคิวแบบไม่ใช้อะตอมจะไม่ปลอดภัยต่อเธรดโดยอัตโนมัติ ตัวอย่างเช่นการดำเนินการต่อไปนี้ไม่ใช่เธรดที่ปลอดภัยโดยอัตโนมัติ:

if(!queue.isEmpty()) {
   queue.poll(obj);
}

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

synchronized(queue) {
    if(!queue.isEmpty()) {
       queue.poll(obj);
    }
}

อีกครั้ง ... การเรียกอะตอมไปยังคิวจะปลอดภัยต่อเธรดโดยอัตโนมัติ การเรียกที่ไม่ใช่อะตอมไม่ได้


1
การใช้งานทั่วไปเพียงอย่างเดียวที่ฉันทำได้คือการบล็อกจนกว่าคิวจะไม่ว่างเปล่า จะไม่ให้บริการที่ดีกว่าโดยใช้การใช้งาน BlockingQueue (take () เป็นอะตอมและบล็อกจนกว่าจะมีสิ่งที่จะบริโภค)?
Adam Jaskiewicz

6
นอกจากนี้คุณต้องระวังด้วย ซึ่งแตกต่างจากซิงโครไนซ์ลิสต์ ConcurrentLinkedQueue จะไม่ซิงโครไนซ์ด้วยตัวมันเองดังนั้นในโค้ดของคุณจึงยังคงเป็นไปได้ที่ผู้ผลิตจะเสนอคิวในขณะที่คุณอยู่ในบล็อกที่ซิงโครไนซ์ของคุณ
Adam Jaskiewicz

ทำไมคุณถึงรวม Queue.isEmpty () และ que.poll () คุณไม่สามารถสำรวจและตรวจสอบว่าผลลัพธ์เป็นโมฆะได้หรือไม่? (ความเข้าใจของฉันคือถ้าคุณสำรวจคิวว่างมันจะส่งกลับค่าว่าง)
AjahnCharles

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


6

นี่อาจเป็นสิ่งที่คุณกำลังมองหาในแง่ของความปลอดภัยและ "ความสวย" เมื่อพยายามกินทุกอย่างในคิว:

for (YourObject obj = queue.poll(); obj != null; obj = queue.poll()) {
}

สิ่งนี้จะรับประกันได้ว่าคุณจะออกจากคิวเมื่อคิวว่างและคุณยังคงแสดงวัตถุต่อไปตราบเท่าที่ยังไม่ว่างเปล่า


มีประโยชน์มากสำหรับการล้างคิว ขอบคุณ.
DevilCode

2

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


1

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

แก้ไข: ConcurrentLinkedList ไม่ได้เป็นเพียง Wrapper แต่เป็นการใช้งานพร้อมกันที่ดีกว่า ไม่ว่าจะด้วยวิธีใดคุณไม่ต้องกังวลเกี่ยวกับการซิงโครไนซ์


ConcurrentLinkedQueue ไม่ได้ มันถูกสร้างขึ้นโดยเฉพาะสำหรับการเข้าถึงพร้อมกันของผู้ผลิตและผู้บริโภคหลายราย แปลกกว่าเครื่องห่อแบบธรรมดาที่ Collections.synchronized *
Adam Jaskiewicz

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