ความแตกต่างระหว่างสถานะของเธรด WAIT และ BLOCKED


102

ความแตกต่างระหว่างสถานะเธรด WAIT และสถานะเธรดถูกบล็อกคืออะไร?

เอกสาร Thread.State :

ถูกบล็อก
เธรดที่ถูกบล็อกเพื่อรอการล็อกจอภาพอยู่ในสถานะนี้

กำลังรอ
เธรดที่กำลังรอเธรดอื่นอย่างไม่มีกำหนดเพื่อดำเนินการเฉพาะอยู่ในสถานะนี้

ไม่ได้อธิบายความแตกต่างให้ฉัน


ตรวจคำตอบในกระทู้นี้stackoverflow.com/questions/2534147/java-thread-wait-blockedนอกจากนี้ลิงค์นี้อาจให้คำอธิบายเพิ่มเติมgeekexplains.blogspot.cz/2008/07/…
อับดุล

@Abdul ลิงก์ geekexplains บอกว่าเธรดสามารถเข้าสู่สถานะที่ถูกบล็อกได้โดยเรียก Object.wait () ไม่ถูกต้องใช่หรือไม่
เกินห้า

อ้างอิงจาก oracle docs docs.oracle.com/javase/6/docs/api/java/lang/… : เธรดอยู่ในสถานะรอเนื่องจากการเรียกใช้วิธีใดวิธีหนึ่งต่อไปนี้ Object.wait โดยไม่มีการหมดเวลา Thread.join โดยไม่หมดเวลา LockSupport.park
อับดุล

สำหรับบันทึกฉันคิดว่าคำตอบของ @ Flavio ดีกว่า Ankit เล็กน้อยในกรณีที่คุณอาจพิจารณาเปลี่ยนแปลง
สีเทา

คำตอบ:


80

เธรดจะเข้าสู่สถานะรอเมื่อเรียกwait()ใช้อ็อบเจ็กต์ เรียกว่าสถานะรอ เมื่อเธรดถึงสถานะรอเธรดจะต้องรอจนกว่าจะมีการเรียกเธรดอื่น ๆnotify()หรือnotifyAll()บนอ็อบเจ็กต์

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

เมื่อเธรดอื่น ๆ ออกไปและโอกาสของเธรดนี้มันจะย้ายไปยังสถานะ Runnable หลังจากนั้นจึงมีสิทธิ์รับงานตามกลไกการทำงานของเธรด JVM และย้ายเพื่อรันสถานะ


2
คุณอธิบายได้ดีกว่ามากเพราะคุณอธิบายลำดับที่เธรดไปถึงสองสถานะนั้นซึ่งทำให้ชัดเจนกว่าการอธิบายแต่ละสถานะแยกกัน (ซึ่งทำได้โดยคำตอบของ "มากกว่าห้า"
Kumar Manish

7
สำหรับทุกคนที่สงสัยว่าทำไมส่วนใหญ่ (ทั้งหมด?) ของไดอะแกรมสถานะที่พบในการอ้างสิทธิ์ทางเว็บการแจ้งเตือน () / แจ้งเตือนทั้งหมด () ส่งผลให้ RUNNABLE แทนที่จะเป็น BLOCKED: stackoverflow.com/questions/28378592/…
Niklas Peter

สมมติว่ามีเธรดเดียวและรอเป็นมิลลิวินาที ตอนนี้เป็นไปได้ไหมที่เธรดสามารถโดยตรงจากสถานะรอเพื่อไปยังสถานะที่รันได้? เนื่องจากไม่มีเธรดอื่นใดล็อคที่นี่เนื่องจากมีเธรดเดียวเท่านั้น?
Kanagavelu Sugumar

มีวิธีการรอ (เวลา) ซึ่งจะกลับสู่สถานะที่รันได้เมื่อเวลาผ่านไป แต่ถ้าไม่มีการระบุเวลาก็จะรอจนกว่าเธรดอื่นจะแจ้งหรือเธรดถูกขัดจังหวะ
Ankit Bansal

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

90

ความแตกต่างค่อนข้างง่าย

ในBLOCKEDสถานะเธรดกำลังจะเข้าสู่synchronizedบล็อก แต่มีเธรดอื่นกำลังทำงานอยู่ภายในsynchronizedบล็อกบนอ็อบเจ็กต์เดียวกัน จากนั้นเธรดแรกจะต้องรอให้เธรดที่สองออกจากบล็อก

ในWAITINGสถานะเธรดกำลังรอสัญญาณจากเธรดอื่น นี้เกิดขึ้นโดยทั่วไปเรียกหรือObject.wait() Thread.join()จากนั้นเธรดจะยังคงอยู่ในสถานะนี้จนกว่าเธรดอื่นจะเรียกObject.notify()หรือตาย


2
ถูกต้องหรือไม่ที่จะบอกว่ามีเพียงเธรดเท่านั้นที่สามารถทำให้รอได้ Thread-B สามารถทำให้ Thread-A เข้าสู่สถานะ WAIT ได้หรือไม่?
เกินห้า

1
คุณแทบจะไม่ได้ใช้Object.wait()โดยตรง แต่คุณจะอยู่ในWAITINGสถานะที่ใช้โครงสร้างการทำงานพร้อมกันระดับสูงเช่นการล็อกคิวการบล็อก ฯลฯ ... พูดอย่างกว้าง ๆ เมื่อใดก็ตามที่สองเธรดต้องประสานกัน
Flavio

1
จากประสบการณ์ส่วนตัวเธรดที่รอ IO (เช่นการอ่านจาก Socket) อยู่ในRUNNINGสถานะ
Flavio

4
เอกสาร Java8 สำหรับThread.Stateกล่าวว่า "... สถานะเหล่านี้เป็นสถานะเครื่องเสมือนซึ่งไม่สะท้อนสถานะเธรดของระบบปฏิบัติการใด ๆ " กล่าวอีกนัยหนึ่ง JVM ไม่สนใจเกี่ยวกับความแตกต่างระหว่างเธรดที่รันโค้ด Java เธรดที่รอการเรียกระบบเพื่อส่งคืนหรือเธรดที่รอการแบ่งเวลา ทั้งหมดRUNNABLEนี้เป็นเรื่องที่ JVM เกี่ยวข้อง
Solomon Slow

3
อาจเป็นการดีที่จะเพิ่มว่าเมื่อเธรดย้ายจากWAITINGสถานะเธรดจะต้องไปที่BLOCKEDสถานะก่อนจนกว่าจะได้รับการล็อกที่เกี่ยวข้องกับอ็อบเจ็กต์ที่รออยู่
สีเทา

23

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

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

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

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


นั่นเป็นเพราะเธรดอื่น ๆ ที่รับผิดชอบในการโทรแจ้ง () บนอ็อบเจ็กต์มอนิเตอร์หรือไม่?
berimbolo

@berimbolo: ฉันไม่เข้าใจสิ่งที่คุณกำลังถาม
นาธานฮิวจ์

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

@berimbolo: ในที่สุดเธรดที่รอคอยก็จะถูกปลุกโดยการแจ้งเตือน ตัวกำหนดตารางเวลาจะตัดสินใจว่าเธรดรอใดที่ได้รับแจ้ง
Nathan Hughes

นับบางสิ่งบางอย่างคุณกำลังพูดว่าสปินล็อคปริมาณที่ถูกบล็อกไม่ได้หมายความว่าเป็นสปินล็อค
แฟรงก์จาง

16

มุมมองที่ง่ายขึ้นสำหรับการตีความการทิ้งเธรด:

  • รอ - ฉันกำลังรอที่จะได้รับงานบางอย่างดังนั้นตอนนี้ฉันไม่ได้ใช้งาน
  • บล็อก - ฉันกำลังยุ่งอยู่กับการพยายามทำงานให้เสร็จ แต่มีเธรดอื่นมาขวางทางฉันดังนั้นฉันจึงไม่ได้ใช้งานในตอนนี้
  • RUNNABLE ... (Native Method) - ฉันเรียกร้องให้ RUN โค้ดเนทีฟบางอย่าง (ซึ่งยังไม่เสร็จสิ้น) เท่าที่เกี่ยวข้องกับ JVM คุณจะทำงานได้และไม่สามารถให้ข้อมูลเพิ่มเติมได้ ตัวอย่างทั่วไปจะเป็นวิธีการฟังซ็อกเก็ตเนทีฟที่เข้ารหัสใน C ซึ่งกำลังรอให้มีการรับส่งข้อมูลเข้ามาดังนั้นฉันจึงไม่ได้ใช้งานในขณะนี้ ในสถานการณ์นั้นสิ่งนี้สามารถมองได้ว่าเป็นการรอแบบพิเศษเนื่องจากเราไม่ได้ทำงานจริง ๆ (ไม่มีการเบิร์น CPU) เลย แต่คุณต้องใช้การถ่ายโอนข้อมูลเธรดของระบบปฏิบัติการแทนการถ่ายโอนข้อมูลเธรด Java เพื่อดู

1
ฉันชอบคำอธิบายของคุณ นั่นคือสิ่งที่ฉันกำลังพยายามทำในการวิเคราะห์การทิ้งเธรดตอนนี้ :)
Sridhar Sarnobat

@MuhammadGelbana ใช่คุณพูดถูกฉันลบความคิดเห็นแล้ว
Eric Wang

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

1

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


-1

ดูตัวอย่างนี้:

การสาธิตสถานะเธรด

/*NEW- thread object created, but not started.
RUNNABLE- thread is executing.
BLOCKED- waiting for monitor after calling wait() method.
WAITING- when wait() if called & waiting for notify() to be called.
  Also when join() is called.
TIMED_WAITING- when below methods are called:
 Thread.sleep
 Object.wait with timeout
 Thread.join with timeout
TERMINATED- thread returned from run() method.*/
public class ThreadBlockingState{

public static void main(String[] args) throws InterruptedException {
    Object obj= new Object();
    Object obj2 = new Object();
    Thread3 t3 = new Thread3(obj,obj2);
    Thread.sleep(1000);
    System.out.println("nm:"+t3.getName()+",state:"+t3.getState().toString()+
            ",when Wait() is called & waiting for notify() to be called.");
    Thread4 t4 = new Thread4(obj,obj2);
    Thread.sleep(3000);
    System.out.println("nm:"+t3.getName()+",state:"+t3.getState().toString()+",After calling Wait() & waiting for monitor of obj2.");
    System.out.println("nm:"+t4.getName()+",state:"+t4.getState().toString()+",when sleep() is called.");
}

}
class Thread3 extends Thread{
Object obj,obj2;
int cnt;
Thread3(Object obj,Object obj2){
    this.obj = obj;
    this.obj2 = obj2;
    this.start();
}

@Override
public void run() {
    super.run();
    synchronized (obj) {
        try {
            System.out.println("nm:"+this.getName()+",state:"+this.getState().toString()+",Before Wait().");
            obj.wait();             
            System.out.println("nm:"+this.getName()+",state:"+this.getState().toString()+",After Wait().");
            synchronized (obj2) {
                cnt++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}
class Thread4 extends Thread{
Object obj,obj2;
Thread4(Object obj,Object obj2){
    this.obj = obj;
    this.obj2 = obj2;
    this.start();
}

@Override
public void run() {
    super.run();
    synchronized (obj) {
        System.out.println("nm:"+this.getName()+",state:"+this.getState().toString()+",Before notify().");
        obj.notify();
        System.out.println("nm:"+this.getName()+",state:"+this.getState().toString()+",After notify().");
    }
    synchronized (obj2) {
        try {
            Thread.sleep(15000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

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