วิธีหลีกเลี่ยงการหมุนอย่างยุ่งใน Java

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

รอรหัสเธรด:

while(true)
{
  if(helloArrived())
    System.out.println("Got hello");
  if(byeArrived())
    System.out.println("Got bye");
  if(stopArrived())
    break;
}

ฉันต้องการหลีกเลี่ยงเทคนิคการ hogging cpu นี้และใช้อย่างอื่นแทน มีความคิดอะไรบ้าง?

แก้ไข: รหัสจริงอยู่ด้านล่าง:

BlockingQueue<Mail> killMeMailbox = new LinkedBlockingQueue<Mail>();
BlockingQueue<Mail> messageMailbox = new LinkedBlockingQueue<Mail>();

public void run()
    {
        while(true)
        {
            if(killMeMailbox.size() > 0)
            {
                break;
            }
            if(messageMailbox.size() > 0)
            {
              System.out.println(messageMailbox.poll());
            }
        }
     }

public void receiveMail(Mail mail)
    {
        //kill
        if(mail.from == -1)
        {
            killMeMailbox.add(0);
        }
        else
        {
            //other
            try
            {
                messageMailbox.put(mail);
            }
            catch(Exception e)
            {
                System.out.println(e.getMessage());
            }
        }
    }

person Josh    schedule 24.10.2014    source แหล่งที่มา
comment
ปัญหาผู้บริโภคของผู้ผลิต คุณได้ลอง Google เพื่อนของคุณหรือยัง?   -  person SMA    schedule 24.10.2014
comment
docs.oracle.com/javase/tutorial/essential/concurrency/   -  person Victor Sorokin    schedule 24.10.2014
comment
คุณต้องมีคิวข้อความ คุณสามารถใช้ BlockingQueue ได้ ดังนั้นเธรดที่รอจะถูกบล็อกจนกว่าข้อความจะมาถึง   -  person Leonidos    schedule 24.10.2014
comment
ฉันใช้โครงสร้างข้อมูลนั้นจริงๆ แต่ฉันกำลังตรวจสอบขนาดในฟังก์ชั่นและล้างมันเพื่อเป็นตัวจัดการมัน มันผิดเหรอ?   -  person Josh    schedule 24.10.2014
comment
@Josh แสดงรหัสของคุณว่าคุณใช้คิวอยู่ที่ไหน   -  person Kayaman    schedule 24.10.2014
comment
ฉันเพิ่งเพิ่มรหัส   -  person Josh    schedule 24.10.2014


คำตอบ (4)


วิธีที่ถูกต้องในการหลีกเลี่ยงปัญหานี้คือการใช้กลไกการรอ/แจ้งเตือนที่ใช้งานโดย java.lang.Object หรือกลไกการทำงานพร้อมกันระดับที่สูงกว่าอย่างใดอย่างหนึ่งที่จัดทำโดยไลบรารีคลาส Java:

(เลือกกลไกที่ตรงกับกรณีการใช้งานของคุณมากที่สุด ...)


การใช้ Thread.sleep ไม่ใช่วิธีแก้ปัญหาที่ดี ในขณะที่คุณลดภาระของ CPU (เมื่อเทียบกับลูปการโพล) ด้านพลิกก็คือคุณลดการตอบสนอง


ตอนนี้ฉันกำลังใช้ BlockingQueue แต่บางทีฉันอาจจะทำมันไม่ถูกต้อง ฉันเพิ่งเพิ่มโค้ดจริงด้านบน คุณเห็นปัญหาของฉันไหม?

ใช่. คุณกำลังใช้คิวในลักษณะที่ออกแบบมาเพื่อหลีกเลี่ยงการบล็อก นั่นเป็นแนวทางที่ผิด คุณควรใช้ take() (ซึ่งจะบล็อกจนกว่ารายการจะพร้อมใช้งาน) แทน poll() และกำจัดโค้ดที่ทดสอบขนาดคิว

ดูเหมือนว่าเนื้อหา "killMeMailbox" ของคุณได้รับการออกแบบมาเพื่อให้คุณไม่ต้องรอเมลอีกต่อไป คุณควรจะสามารถใช้งานได้โดยใช้ Thread.interrupt (การขัดจังหวะจะปลดบล็อกการโทร take() ... )

person Stephen C    schedule 24.10.2014
comment
ฉันขอแนะนำให้พูดถึง LockSupport แทนที่จะรอ/แจ้งเตือน เป็นการทดแทนแบบ 1 ต่อ 1 สำหรับการรอ/แจ้งเตือน แต่ไม่ได้รับผลกระทบจากสภาพการแข่งขัน - person Marko Topolnik; 24.10.2014
comment
ตอนนี้ฉันกำลังใช้ BlockingQueue แต่บางทีฉันอาจจะทำมันไม่ถูกต้อง ฉันเพิ่งเพิ่มโค้ดจริงด้านบน คุณเห็นปัญหาของฉันไหม? - person Josh; 24.10.2014
comment
@MarkoTopolnik - ฉันได้ยินสิ่งที่คุณพูด แต่ฉันไม่คุ้นเคย ฉันขอแนะนำให้คุณเพิ่มคำตอบของคุณเองที่อธิบายวิธีใช้งาน - person Stephen C; 24.10.2014
comment
สมบูรณ์แบบ! Take() คือสิ่งที่ฉันขาดหายไป ขอบคุณ! - person Josh; 24.10.2014
comment
กลไกเหล่านั้นอยู่ในระดับต่ำเกินไปดังนั้นจึงไม่มีประโยชน์ที่จะเขียนคำตอบทั้งหมดเพียงเพื่อสิ่งนั้น แต่เนื่องจาก wait/notify เลิกใช้งานแล้ว (คำว่า ท้อแท้) ฉันจึงไม่พูดถึงมัน - person Marko Topolnik; 24.10.2014

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

  • ใช้ เซมาฟอร์
  • ใช้วิธีการซิงโครไนซ์กับ รอ /แจ้งเตือน
  • สำหรับการแลกเปลี่ยนข้อความระหว่างเธรดโดยเฉพาะ โดยใช้ BlockingQueues อาจเป็นตัวเลือกที่ดีที่สุด
person ChrisJ    schedule 24.10.2014

หากคุณสามารถจัดรูปแบบปัญหาของคุณใหม่ในแง่ของ Task-executor ให้ลองใช้ SingleThreadExecutor หากคุณต้องการสิ่งที่แปลกใหม่กว่านี้ - คิวที่เกิดขึ้นพร้อมกัน หรือแม้แต่ wait()/notify() .

person bobah    schedule 24.10.2014

คุณได้ลองใส่ Thread.sleep บางส่วนเพื่อหลีกเลี่ยงการดำเนินการ while วนซ้ำอย่างต่อเนื่องหรือไม่? มันจะปลดปล่อย CPU สำหรับเธรดอื่นของคุณและหลีกเลี่ยงปัญหาหมู

http://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html

person vicsana1    schedule 24.10.2014
comment
แม้ว่าการใช้ sleep อาจใช้ได้ในบางกรณี แต่การใช้ notifyAll()/wait() จะดีกว่าด้วยเหตุผลด้านการตอบสนอง - person Victor Sorokin; 24.10.2014
comment
จากนั้นให้พักเวลาเล็กน้อยหรือใช้โครงสร้างข้อมูลอื่น ฉันคิดว่ามีโครงสร้างบางอย่างเช่น BlockingQueue ที่อนุญาตให้เธรดรอจนกว่าจะมีบางอย่างอยู่ในคิว - person vicsana1; 24.10.2014