ถูกกฎหมายหรือไม่ที่จะเรียกวิธีการเริ่มต้นสองครั้งบนเธรดเดียวกัน


90

รหัสต่อไปนี้นำไปสู่java.lang.IllegalThreadStateException: Thread already startedเมื่อฉันเรียกstart()method ครั้งที่สองในโปรแกรม

updateUI.join();    

if (!updateUI.isAlive()) 
    updateUI.start();

นี้เกิดขึ้นสองครั้งที่updateUI.start()ถูกเรียกว่า updateUI.start()ฉันได้ก้าวผ่านมันหลายครั้งและด้ายที่เรียกว่าสมบูรณ์และทำงานให้เสร็จก่อนกดปุ่ม

การโทรupdateUI.run()หลีกเลี่ยงข้อผิดพลาด แต่ทำให้เธรดทำงานในเธรด UI (เธรดการเรียกตามที่กล่าวไว้ในโพสต์อื่น ๆ บน SO) ซึ่งไม่ใช่สิ่งที่ฉันต้องการ

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


9
ทำไมคุณไม่อ่าน th javadoc - มันอธิบายถึงสัญญาอย่างชัดเจน
mP.

คำตอบ:


112

จากข้อกำหนดJava APIสำหรับThread.startวิธีการ:

ไม่เคยถูกกฎหมายที่จะเริ่มเธรดมากกว่าหนึ่งครั้ง โดยเฉพาะอย่างยิ่งเธรดอาจไม่ถูกรีสตาร์ทเมื่อดำเนินการเสร็จสิ้น

นอกจากนี้:

พ่น:
IllegalThreadStateException- ถ้าเธรดเริ่มต้นแล้ว

ใช่Threadสามารถเริ่มต้นได้เพียงครั้งเดียว

ถ้าเป็นเช่นนั้นฉันจะทำอย่างไรหากต้องการเรียกใช้เธรดอีกครั้ง

หากThreadจำเป็นต้องเรียกใช้มากกว่าหนึ่งครั้งควรสร้างอินสแตนซ์ใหม่Threadและเรียกstartใช้


ขอบคุณ. ฉันตรวจสอบเอกสารด้วย IDE และบทช่วยสอน Java สำหรับเธรด (และ Google ด้วย) ฉันจะตรวจสอบข้อมูลจำเพาะของ API ในอนาคต ที่สำคัญ ".. ไม่เคยถูกกฎหมายที่จะเริ่มต้นมากกว่าหนึ่งครั้ง .. " ไม่ได้อยู่ในการอ่านอื่น ๆ
จะ

@coobird ถ้าฉันกำหนดชื่ออ็อบเจ็กต์เธรดเก่าให้กับเธรดใหม่ () หลังจากเธรดเก่าเสร็จสิ้นเธรดเก่าจะถูกรวบรวมขยะ (กล่าวคือถูกรีไซเคิลโดยอัตโนมัติหรือต้องทำอย่างชัดเจน)?
snapfractalpop

มันจะถูกเก็บรวบรวมตราบใดที่เธรดไม่ทำงานอีกต่อไป
บินไป

1
คำตอบนี้ล้าสมัยไปหน่อย หากโปรแกรม Java ที่ทันสมัยจำเป็นต้องดำเนินการงานมากกว่าหนึ่งครั้งแล้วก็ไม่ควรสร้างใหม่Threadในแต่ละครั้ง แต่ควรส่งงานไปยังกลุ่มเธรด (เช่นjava.util.concurrent.ThreadPoolExecutor)
Solomon Slow

13

ถูกต้อง จากเอกสารประกอบ :

ไม่เคยถูกกฎหมายที่จะเริ่มเธรดมากกว่าหนึ่งครั้ง โดยเฉพาะอย่างยิ่งเธรดอาจไม่ถูกรีสตาร์ทเมื่อดำเนินการเสร็จสิ้น

ในแง่ของสิ่งที่คุณสามารถทำได้ในการคำนวณซ้ำดูเหมือนว่าคุณสามารถใช้วิธีการ SwingUtilities invokeLater คุณได้ทดลองกับโทรrun()โดยตรงซึ่งหมายความว่าคุณกำลังแล้วคิดเกี่ยวกับการใช้มากกว่าดิบRunnable Threadลองใช้invokeLaterวิธีนี้กับRunnableงานและดูว่าเหมาะกับรูปแบบจิตใจของคุณหรือไม่

นี่คือตัวอย่างจากเอกสาร:

 Runnable doHelloWorld = new Runnable() {
     public void run() {
         // Put your UI update computations in here.
         // BTW - remember to restrict Swing calls to the AWT Event thread.
         System.out.println("Hello World on " + Thread.currentThread());
     }
 };

 SwingUtilities.invokeLater(doHelloWorld);
 System.out.println("This might well be displayed before the other message.");

หากคุณแทนที่การprintlnเรียกนั้นด้วยการคำนวณของคุณอาจเป็นสิ่งที่คุณต้องการ

แก้ไข: ติดตามความคิดเห็นฉันไม่สังเกตเห็นแท็ก Android ในโพสต์ต้นฉบับ เท่ากับ invokeLater ในการทำงาน Android Handler.post(Runnable)เป็น จาก javadoc:

/**
 * Causes the Runnable r to be added to the message queue.
 * The runnable will be run on the thread to which this handler is
 * attached.
 *
 * @param r The Runnable that will be executed.
 *
 * @return Returns true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */

ดังนั้นในโลกของ Android คุณสามารถใช้ตัวอย่างเดียวกับด้านบนโดยแทนที่Swingutilities.invokeLaterด้วยโพสต์ที่เหมาะสมเป็นHandlerไฟล์.


OP กำลังถามเกี่ยวกับเธรดบน Android ซึ่งไม่รวมถึง SwingUtilities
Austyn Mahoney

@Austyn คุณพูดถูก ฉันเพิ่มข้อสังเกตเกี่ยวกับ Handler.post () เพื่อแสดงโค้ด Android แบบขนาน
Bob Cross

1
อีกวิธีหนึ่งหากคุณกำลังพยายามอัปเดต UI คือการใช้RunOnUIThread(Runnable)หรือView.post(Runnable)แทนที่จะสร้างตัวจัดการของคุณเอง สิ่งเหล่านี้จะเรียกใช้ runnable บนเธรดหลักเพื่อให้คุณอัปเดต UI
Austyn Mahoney

3

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

เธรดนี้กำลังทำการคำนวณบางอย่างในพื้นหลังหากฉันไม่ทำในเธรดมากกว่าที่ทำในเธรด UI และผู้ใช้ต้องรอนานอย่างไม่มีเหตุผล

AsyncTaskการถ่ายโอนข้อมูลของคุณเองด้ายและการใช้งาน

หรือสร้างด้ายใหม่เมื่อคุณต้องการ

หรือตั้งค่าเธรดของคุณเพื่อดำเนินการนอกคิวงาน (เช่นLinkedBlockingQueue) แทนที่จะรีสตาร์ทเธรด


3

ไม่เราไม่สามารถเริ่ม Thread ได้อีกครั้งการทำเช่นนั้นจะทำให้ runtimeException java.lang.IllegalThreadStateException >

เหตุผลคือเมื่อรัน () เมธอดถูกดำเนินการโดยเธรดมันจะเข้าสู่สถานะตาย

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

public class MyClass implements Runnable{

    @Override
    public void run() {
           System.out.println("in run() method, method completed.");
    }

    public static void main(String[] args) {
                  MyClass obj=new MyClass();            
        Thread thread1=new Thread(obj,"Thread-1");
        thread1.start();
        thread1.start(); //will throw java.lang.IllegalThreadStateException at runtime
    }

}

/ * OUTPUT ใน run () วิธีการเสร็จสิ้น ข้อยกเว้นในเธรด "main" java.lang.IllegalThreadStateException ที่ java.lang.Thread.start (ไม่ทราบแหล่งที่มา) * /

ตรวจสอบสิ่งนี้


2

สิ่งที่คุณควรทำคือสร้าง Runnable และพันด้วย Thread ใหม่ทุกครั้งที่คุณต้องการเรียกใช้ Runnable มันจะน่าเกลียดมากที่จะทำ แต่คุณสามารถตัดเธรดด้วยเธรดอื่นเพื่อรันโค้ดอีกครั้ง แต่คุณต้องทำสิ่งนี้จริงๆเท่านั้น


snipet ใด ๆ ห่อยังไง?
Vinay

1

ตามที่คุณกล่าวไว้ไม่สามารถเริ่มเธรดได้มากกว่าหนึ่งครั้ง

ตรงจากปากม้า: Java API Spec

ไม่เคยถูกกฎหมายที่จะเริ่มเธรดมากกว่าหนึ่งครั้ง โดยเฉพาะอย่างยิ่งเธรดอาจไม่ถูกรีสตาร์ทเมื่อดำเนินการเสร็จสิ้น

หากคุณต้องการเรียกใช้สิ่งที่เกิดขึ้นในเธรดของคุณอีกครั้งคุณจะต้องสร้างเธรดใหม่และเรียกใช้สิ่งนั้น


0

การใช้เธรดซ้ำเป็นการกระทำที่ผิดกฎหมายใน Java API อย่างไรก็ตามคุณสามารถรวมเข้ากับการใช้งานที่รันได้และเรียกใช้อินสแตนซ์นั้นอีกครั้ง


0

ใช่เราไม่สามารถเริ่มทำงานเธรดได้แล้ว มันจะโยน IllegalThreadStateException ที่รันไทม์ - ถ้าเธรดเริ่มต้นแล้ว

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


0

เธรดสามารถเริ่มต้นได้เพียงครั้งเดียวหรือไม่?

ใช่. คุณสามารถเริ่มได้ครั้งเดียว

ถ้าเป็นเช่นนั้นฉันจะทำอย่างไรหากต้องการเรียกใช้เธรดอีกครั้งเธรดเฉพาะนี้กำลังทำการคำนวณบางอย่างอยู่เบื้องหลังถ้าฉันไม่ทำในเธรดมากกว่าที่ทำในเธรด UI และผู้ใช้ไม่มีเหตุผล รอนาน.

อย่าเรียกใช้Threadอีกครั้ง แทนที่จะสร้างRunnableและโพสต์บนHandlerของHandlerThread คุณสามารถส่งหลายRunnableวัตถุ หากต้องการส่งข้อมูลกลับไปที่เธรด UI โดยใช้Runnable run()วิธีการของคุณ ให้โพสต์MessageบนHandlerUI เธรดและดำเนินการhandleMessage

อ้างถึงโพสต์นี้สำหรับรหัสตัวอย่าง:

Android: ปิ้งในกระทู้


0

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

ฉันรู้ว่ามันจะไม่ได้เริ่มกระทู้อีกครั้ง แต่อาจจะมีประโยชน์สำหรับคุณ

public void run() {

    LifeCycleComponent lifeCycleComponent = new LifeCycleComponent();

    try {
        NetworkState firstState = lifeCycleComponent.getCurrentNetworkState();
        Thread.sleep(5000);
        if (firstState != lifeCycleComponent.getCurrentNetworkState()) {
            System.out.println("{There was a NetworkState change!}");
            run();
        } else {
            run();
        }
    } catch (SocketException | InterruptedException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    Thread checkingNetworkStates = new Thread(new LifeCycleComponent());
    checkingNetworkStates.start();
}

หวังว่านี่จะช่วยได้แม้ว่าจะเป็นเพียงเล็กน้อยก็ตาม

ไชโย


-1

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

ฉันต้องแก้ไขการรั่วไหลของทรัพยากรที่เกิดจากโปรแกรมเมอร์ที่สร้าง Thread แต่แทนที่จะเริ่มต้น () เข้ามาเขาเรียกว่า run () - method โดยตรง ดังนั้นหลีกเลี่ยงมันเว้นแต่คุณจะรู้จริงๆว่ามันทำให้เกิดผลข้างเคียงอะไร


แต่ข้อเสนอแนะก็จะไม่เรียกrun()โดยตรงเพียงเพื่อฝัง Runnable start()ในกระทู้และสันนิษฐานวิงวอน
H2ONaCl

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