Apakah legal untuk memanggil metode start dua kali pada Thread yang sama?

90

Kode berikut mengarah ke java.lang.IllegalThreadStateException: Thread already startedketika saya memanggil start()metode kedua kalinya dalam program.

updateUI.join();    

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

Ini terjadi untuk kedua kalinya updateUI.start()dipanggil. Saya telah melewatinya beberapa kali dan utas dipanggil dan selesai berjalan sampai selesai sebelum memukul updateUI.start().

Memanggil updateUI.run()menghindari kesalahan tetapi menyebabkan utas berjalan di utas UI (utas pemanggil, seperti yang disebutkan dalam posting lain di SO), yang bukan itu yang saya inginkan.

Bisakah sebuah Thread dimulai hanya sekali? Jika ya, apa yang harus saya lakukan jika saya ingin menjalankan utas lagi? Utas khusus ini melakukan beberapa kalkulasi di latar belakang, jika saya tidak melakukannya di utas daripada yang dilakukan di utas UI dan pengguna harus menunggu terlalu lama.

Akan
sumber
9
Mengapa Anda tidak membaca saja javadoc - itu dengan jelas menggambarkan kontrak.
mP.

Jawaban:

112

Dari Spesifikasi Java API untuk Thread.startmetode:

Memulai utas lebih dari sekali tidak pernah legal. Secara khusus, utas tidak dapat dimulai ulang setelah menyelesaikan eksekusi.

Selanjutnya:

Melempar:
IllegalThreadStateException- jika utas sudah dimulai.

Jadi ya, a Threadhanya bisa dimulai sekali.

Jika ya, apa yang harus saya lakukan jika saya ingin menjalankan utas lagi?

Jika a Threadperlu dijalankan lebih dari sekali, maka seseorang harus membuat instance baru dari Threaddan memanggilnya start.

burung coobird
sumber
Terima kasih. Saya memeriksa dokumentasi dengan IDE dan tutorial Java untuk utas (dan juga google). Saya akan memeriksa spesifikasi API di masa mendatang. Yang kritis "..tidak pernah legal untuk memulai lebih dari sekali .." tidak dalam bacaan lain.
Akankah
@ coobird, jika saya menetapkan nama objek utas lama ke utas baru (), setelah utas lama selesai, apakah utas lama akan dikumpulkan sampah (yaitu apakah secara otomatis didaur ulang, atau haruskah ini dilakukan secara eksplisit)?
snapfractalpop
Ini akan mengumpulkan sampah, selama Thread tidak lagi berjalan.
terbang
1
Jawaban ini agak ketinggalan zaman. Jika program Java modern perlu melakukan tugas lebih dari sekali, maka program tersebut tidak boleh membuat yang baru Threadsetiap kali. Sebaliknya, itu harus mengirimkan tugas ke kumpulan benang (misalnya, java.util.concurrent.ThreadPoolExecutor)
Solomon Slow
13

Benar sekali. Dari dokumentasi :

Memulai utas lebih dari sekali tidak pernah legal. Secara khusus, utas tidak dapat dimulai ulang setelah menyelesaikan eksekusi.

Dalam hal apa yang dapat Anda lakukan untuk komputasi berulang, sepertinya Anda dapat menggunakan metode invokeLater SwingUtilities . Anda sudah bereksperimen dengan menelepon run()secara langsung, artinya Anda sudah berpikir untuk menggunakan a Runnabledaripada mentah Thread. Coba gunakan invokeLatermetode ini hanya pada Runnabletugas dan lihat apakah itu sesuai dengan pola mental Anda.

Berikut contoh dari dokumentasinya:

 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.");

Jika Anda mengganti printlnpanggilan itu dengan komputasi Anda, mungkin itu yang Anda butuhkan.

EDIT: menindaklanjuti komentar, saya tidak melihat tag Android di postingan asli. Setara dengan invokeLater di pekerjaan Android adalah Handler.post(Runnable). Dari javadocnya:

/**
 * 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.
 */

Jadi, di dunia Android, Anda dapat menggunakan contoh yang sama seperti di atas, mengganti Swingutilities.invokeLaterdengan postingan yang sesuai menjadi file Handler.

Bob Cross
sumber
OP menanyakan tentang threading di Android, yang tidak menyertakan SwingUtilities.
Austyn Mahoney
@Ayn, kamu benar. Saya menambahkan komentar tentang Handler.post () untuk mengilustrasikan kode Android paralel.
Bob Cross
1
Cara lain jika Anda hanya mencoba memperbarui UI Anda adalah dengan menggunakan RunOnUIThread(Runnable)atau View.post(Runnable)alih-alih membuat Handler Anda sendiri. Ini akan menjalankan runnable pada utas utama yang memungkinkan Anda memperbarui UI.
Austyn Mahoney
3

Jawaban yang baru saja tiba mencakup mengapa Anda tidak boleh melakukan apa yang Anda lakukan. Berikut beberapa opsi untuk menyelesaikan masalah Anda yang sebenarnya.

Utas khusus ini melakukan beberapa kalkulasi di latar belakang, jika saya tidak melakukannya di utas daripada yang dilakukan di utas UI dan pengguna harus menunggu terlalu lama.

Buang utas Anda sendiri dan gunakan AsyncTask.

Atau buat utas baru saat Anda membutuhkannya.

Atau atur utas Anda agar beroperasi di luar antrean kerja (misalnya, LinkedBlockingQueue) daripada memulai ulang utas.

CommonsWare
sumber
3

Tidak , kita tidak bisa memulai Thread lagi, hal itu akan memunculkan runtimeException java.lang.IllegalThreadStateException. >

Alasannya adalah setelah metode run () dieksekusi oleh Thread, ia menjadi mati.

Mari kita ambil contoh- Berpikir untuk memulai utas lagi dan memanggil metode start () di atasnya (yang secara internal akan memanggil metode run ()) bagi kita adalah beberapa hal seperti meminta orang mati untuk bangun dan lari. Seperti, setelah menyelesaikan hidupnya orang pergi ke keadaan mati.

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 dalam metode run (), metode selesai. Pengecualian di thread "main" java.lang.IllegalThreadStateException di java.lang.Thread.start (Sumber Tidak Dikenal) * /

Periksa ini

Sameer Kazi
sumber
2

Yang harus Anda lakukan adalah membuat Runnable dan membungkusnya dengan Thread baru setiap kali Anda ingin menjalankan Runnable. Ini akan sangat buruk untuk dilakukan tetapi Anda dapat membungkus utas dengan utas lain untuk menjalankan kode untuk itu lagi tetapi hanya melakukan ini adalah Anda benar-benar harus melakukannya.

Peter Lawrey
sumber
ada snipet, bagaimana cara membungkusnya?
Vinay
1

Seperti yang Anda katakan, utas tidak dapat dimulai lebih dari sekali.

Langsung dari mulut kuda: Spesifikasi API Java

Memulai utas lebih dari sekali tidak pernah legal. Secara khusus, utas tidak dapat dimulai ulang setelah menyelesaikan eksekusi.

Jika Anda perlu menjalankan ulang apa pun yang terjadi di utas Anda, Anda harus membuat utas baru dan menjalankannya.

alanlcode
sumber
0

Untuk menggunakan kembali thread adalah tindakan ilegal di Java API. Namun, Anda dapat menggabungkannya menjadi implementasi yang dapat dijalankan dan menjalankan ulang instance itu lagi.

Aaron He
sumber
0

Ya, kami tidak dapat mulai menjalankan utas. Ini akan memunculkan IllegalThreadStateException saat runtime - jika utas sudah dimulai.

Bagaimana jika Anda benar-benar perlu memulai utas: Opsi 1) Jika utas perlu dijalankan lebih dari sekali, maka seseorang harus membuat contoh baru dari utas dan memanggilnya mulai.

riteeka
sumber
0

Bisakah sebuah Thread dimulai hanya sekali?

Iya. Anda bisa memulainya tepat sekali.

Jika demikian dari apa yang harus saya lakukan jika saya ingin menjalankan utas lagi? Utas khusus ini melakukan beberapa perhitungan di latar belakang, jika saya tidak melakukannya di utas daripada yang dilakukan di utas UI dan pengguna memiliki alasan yang tidak masuk akal menunggu lama.

Jangan lari Threadlagi. Sebagai gantinya buat Runnable dan posting di Handler of HandlerThread . Anda dapat mengirimkan banyak Runnableobjek. Jika ingin mengirim data kembali ke UI Thread, dengan-in Runnable run()metode Anda , posting Messagedi HandlerUI Thread dan proseshandleMessage

Lihat posting ini untuk contoh kode:

Android: Bersulang di utas

Ravindra babu
sumber
0

Saya tidak tahu apakah ini praktik yang baik tetapi ketika saya membiarkan run () dipanggil di dalam metode run (), itu tidak menimbulkan kesalahan dan benar-benar melakukan apa yang saya inginkan.

Saya tahu ini tidak memulai utas lagi, tetapi mungkin ini berguna untuk Anda.

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();
}

Semoga ini bisa membantu, meski hanya sedikit.

Bersulang

Diorcula
sumber
-1

Ini akan sangat buruk untuk dilakukan tetapi Anda dapat membungkus utas dengan utas lain untuk menjalankan kode untuk itu lagi tetapi hanya melakukan ini adalah Anda benar-benar harus melakukannya.

Saya harus memperbaiki kebocoran sumber daya yang disebabkan oleh pemrogram yang membuat Thread tetapi alih-alih memulai (), dia memanggil metode run () - secara langsung. Jadi hindarilah, kecuali Anda benar-benar tahu apa efek samping yang ditimbulkannya.

Torben
sumber
Tetapi sarannya bukan untuk menelepon run()secara langsung, hanya untuk menyematkan Runnable di Thread dan mungkin dipanggil start().
H2ONaCl
@ H2ONaCl Jika Anda membaca teks yang saya kutip, sarannya adalah untuk membungkus sebuah Thread dalam sebuah Thread. Mungkin Anda tidak bisa membaca saran asli sebelum diedit.
Torben