Có hợp pháp không khi gọi phương thức bắt đầu hai lần trên cùng một Chủ đề?


89

Đoạn mã sau dẫn đến java.lang.IllegalThreadStateException: Thread already startedkhi tôi gọi start()phương thức lần thứ hai trong chương trình.

updateUI.join();    

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

Điều này xảy ra các thứ hai thời gian updateUI.start()được gọi. Tôi đã bước qua nó nhiều lần và chuỗi được gọi và hoàn thành chạy đến hoàn thành trước khi nhấn updateUI.start().

Việc gọi updateUI.run()tránh được lỗi nhưng khiến luồng chạy trong luồng giao diện người dùng (luồng gọi, như đã đề cập trong các bài đăng khác trên SO), đây không phải là điều tôi muốn.

Có thể bắt đầu một Chủ đề chỉ một lần không? Nếu vậy tôi phải làm gì nếu tôi muốn chạy lại chuỗi? Luồng cụ thể này đang thực hiện một số phép tính ở chế độ nền, nếu tôi không thực hiện trong luồng thì không thực hiện trong luồng giao diện người dùng và người dùng phải đợi lâu một cách vô lý.


9
Tại sao bạn không đọc javadoc thứ - nó mô tả rõ ràng hợp đồng thứ.
mP.

Câu trả lời:


111

Từ Đặc tả API Java cho Thread.startphương thức:

Không bao giờ hợp pháp để bắt đầu một chủ đề nhiều hơn một lần. Đặc biệt, một luồng có thể không được khởi động lại khi nó đã hoàn tất quá trình thực thi.

Hơn nữa:

Ném:
IllegalThreadStateException- nếu chủ đề đã được bắt đầu.

Vì vậy, có, a Threadchỉ có thể được bắt đầu một lần.

Nếu vậy tôi phải làm gì nếu tôi muốn chạy lại chuỗi?

Nếu một Threadcần được chạy nhiều lần, thì người ta nên tạo một phiên bản mới của Threadvà gọi startnó.


Cảm ơn. Tôi đã kiểm tra tài liệu với IDE và hướng dẫn Java cho các luồng (và cả google nữa). Tôi sẽ kiểm tra thông số API trong tương lai. Điều quan trọng ".. không bao giờ hợp pháp để bắt đầu nhiều lần .." không có trong các bài đọc khác.
Sẽ

@coobird, nếu tôi gán tên đối tượng luồng cũ cho Thread () mới, sau khi luồng cũ kết thúc, luồng cũ có được thu gom rác không (nghĩa là nó có tự động được tái chế hay phải thực hiện rõ ràng)?
snapfractalpop

Nó sẽ được thu gom rác, miễn là Thread không còn chạy nữa.
bay

1
Câu trả lời này hơi lỗi thời. Nếu một chương trình Java hiện đại cần thực hiện một tác vụ nhiều hơn một lần, thì nó không nên tạo một tác vụ mới Threadmỗi lần. Thay vào đó, nó phải nộp nhiệm vụ cho một hồ bơi thread (ví dụ java.util.concurrent.ThreadPoolExecutor)
Solomon Chậm

13

Hoàn toàn chính xác. Từ tài liệu :

Không bao giờ hợp pháp để bắt đầu một chủ đề nhiều hơn một lần. Đặc biệt, một luồng có thể không được khởi động lại khi nó đã hoàn tất quá trình thực thi.

Về những gì bạn có thể làm khi tính toán lặp lại, có vẻ như bạn có thể sử dụng phương thức invokeLater của SwingUtilities . Bạn đang thử nghiệm với việc gọi điện run()trực tiếp, có nghĩa là bạn đã nghĩ đến việc sử dụng một cuộc gọi Runnablethay vì một cuộc gọi thô Thread. Hãy thử sử dụng invokeLaterphương pháp trên chỉ cho Runnablenhiệm vụ và xem liệu cách đó có phù hợp với khuôn mẫu tinh thần của bạn hơn một chút hay không.

Đây là ví dụ từ tài liệu:

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

Nếu bạn thay thế nó println cuộc gọi bằng tính toán của mình, nó có thể chính xác là những gì bạn cần.

CHỈNH SỬA: theo dõi nhận xét, tôi đã không nhận thấy thẻ Android trong bài đăng gốc. Tương đương với invokeLater trong công việc Android là Handler.post(Runnable). Từ javadoc của nó:

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

Vì vậy, trong thế giới Android, bạn có thể sử dụng ví dụ tương tự như trên, thay thế Swingutilities.invokeLaterbằng bài đăng thích hợp thành a Handler.


OP đang hỏi về phân luồng trên Android, không bao gồm SwingUtilities.
Austyn Mahoney

@Austyn, bạn nói đúng. Tôi đã thêm nhận xét về Handler.post () để minh họa mã Android song song.
Bob Cross

1
Một cách khác nếu bạn chỉ đang cố gắng cập nhật giao diện người dùng của mình là sử dụng RunOnUIThread(Runnable)hoặc View.post(Runnable)thay vì tạo Trình xử lý của riêng bạn. Những điều này sẽ chạy có thể chạy được trên luồng chính cho phép bạn cập nhật giao diện người dùng.
Austyn Mahoney

3

Câu trả lời vừa đến bao gồm lý do tại sao bạn không nên làm những gì bạn đang làm. Dưới đây là một số tùy chọn để giải quyết vấn đề thực tế của bạn.

Luồng cụ thể này đang thực hiện một số phép tính ở chế độ nền, nếu tôi không thực hiện trong luồng thì không thực hiện trong luồng giao diện người dùng và người dùng phải đợi lâu một cách vô lý.

Dump chủ đề của riêng bạn và sử dụng AsyncTask.

Hoặc tạo một luồng mới khi bạn cần.

Hoặc thiết lập luồng của bạn để hoạt động ngoài hàng đợi công việc (ví dụ LinkedBlockingQueue:) thay vì khởi động lại luồng.


3

Không , chúng ta không thể bắt đầu lại Thread, làm như vậy sẽ ném runtimeException java.lang.IllegalThreadStateException. >

Lý do là một khi phương thức run () được thực thi bởi Thread, nó sẽ chuyển sang trạng thái chết.

Hãy lấy một ví dụ- Việc nghĩ đến việc bắt đầu lại chuỗi và gọi phương thức start () trên nó (nội bộ sẽ gọi phương thức run ()) đối với chúng ta giống như yêu cầu người chết thức dậy và chạy. Như, sau khi hoàn thành cuộc sống của mình, con người chuyển sang trạng thái chết.

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 trong phương thức run (), phương thức đã hoàn thành. Ngoại lệ trong chuỗi "main" java.lang.IllegalThreadStateException tại java.lang.Thread.start (Nguồn không xác định) * /

kiểm tra điều này


2

Những gì bạn nên làm là tạo một Runnable và bọc nó bằng một Thread mới mỗi khi bạn muốn chạy Runnable. Nó sẽ thực sự xấu khi làm nhưng bạn có thể quấn một luồng bằng một luồng khác để chạy lại mã cho nó nhưng chỉ làm điều này là bạn thực sự phải làm.


bất kỳ snipet, làm thế nào để quấn?
Vinay

1

Như bạn đã nói, một chủ đề không thể được bắt đầu nhiều hơn một lần.

Trực tiếp từ miệng ngựa: Thông số API của Java

Không bao giờ hợp pháp để bắt đầu một chủ đề nhiều hơn một lần. Đặc biệt, một luồng có thể không được khởi động lại khi nó đã hoàn tất quá trình thực thi.

Nếu bạn cần chạy lại bất cứ thứ gì đang diễn ra trong luồng của mình, bạn sẽ phải tạo một luồng mới và chạy nó.


0

Sử dụng lại một chuỗi là hành động bất hợp pháp trong Java API. Tuy nhiên, bạn có thể bọc nó thành một triển khai có thể chạy được và chạy lại phiên bản đó một lần nữa.


0

Có, chúng tôi không thể bắt đầu chạy chủ đề. Nó sẽ ném IllegalThreadStateException trong thời gian chạy - nếu luồng đã được bắt đầu.

Điều gì xảy ra nếu bạn thực sự cần Bắt đầu luồng: Tùy chọn 1) Nếu một Luồng cần được chạy nhiều hơn một lần, thì người ta nên tạo một phiên bản mới của Luồng và gọi start trên đó.


0

Có thể bắt đầu một Chủ đề chỉ một lần không?

Đúng. Bạn có thể bắt đầu nó chính xác một lần.

Nếu vậy, tôi phải làm gì nếu tôi muốn chạy lại chuỗi? Chuỗi cụ thể này đang thực hiện một số phép tính trong nền, nếu tôi không thực hiện nó trong chuỗi hơn là thực hiện trong chuỗi giao diện người dùng và người dùng có một đợi lâu.

Đừng chạy Threadlại. Thay vào đó, hãy tạo Runnable và đăng nó trên Handler của HandlerThread . Bạn có thể gửi nhiều Runnableđối tượng. Nếu muốn gửi dữ liệu trở lại Chuỗi giao diện người dùng, với Runnable run()phương pháp của bạn , hãy đăng Messagelên HandlerChủ đề giao diện người dùng và xử lýhandleMessage

Tham khảo bài đăng này để biết mã ví dụ:

Android: Bánh mì nướng trong chuỗi


0

Tôi không biết liệu đó có phải là phương pháp hay không nhưng khi tôi để run () được gọi bên trong phương thức run (), nó không có lỗi và thực sự làm chính xác những gì tôi muốn.

Tôi biết nó không bắt đầu lại một chủ đề, nhưng có lẽ điều này có ích cho bạn.

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

Hy vọng điều này sẽ giúp, ngay cả khi nó chỉ là một chút.

Chúc mừng


-1

Nó sẽ thực sự xấu khi làm nhưng bạn có thể quấn một luồng bằng một luồng khác để chạy lại mã cho nó nhưng chỉ làm điều này là bạn thực sự phải làm.

Tôi đã phải khắc phục sự cố rò rỉ tài nguyên do một lập trình viên đã tạo Thread nhưng thay vì start () ing nó, anh ta đã gọi phương thức run () - trực tiếp. Vì vậy, hãy tránh nó, trừ khi bạn thực sự thực sự biết những tác dụng phụ mà nó gây ra.


Nhưng gợi ý là không gọi run()trực tiếp, chỉ nhúng một Runnable vào một Thread và có lẽ là gọi start().
H2ONaCl

@ H2ONaCl Nếu bạn đọc văn bản tôi đã trích dẫn, gợi ý là hãy gói một Chủ đề trong một Chủ đề. Có thể bạn đã không đọc đề xuất ban đầu trước khi nó được chỉnh sửa.
Torben
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.