2010-04-27 35 views
6

Tôi đang sử dụng một chuỗi liên tục đọc từ hàng đợi.Dừng chuỗi lặp trong Java

Cái gì như:

public void run() { 
    Object obj; 
    while(true) { 
     synchronized(objectsQueue) { 
      if(objectesQueue.isEmpty()) { 
       try { 
        objectesQueue.wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 

       obj = objectesQueue.poll(); 
      } 
     } 

     // Do something with the Object obj 
    } 
} 

cách tốt nhất để ngăn chặn chủ đề này là gì?

tôi thấy hai lựa chọn:

1 - Kể từ Thread.stop() bị phản đối, tôi có thể thực hiện một phương pháp stopThisThread() có sử dụng một n nguyên tử biến kiểm tra điều kiện.

2 - Gửi đối tượng Sự kiện chết hoặc thứ gì đó tương tự như hàng đợi. Khi luồng tìm nạp một sự kiện tử vong, nó thoát ra.

Tôi thích cách thứ nhất, tuy nhiên, tôi không biết khi nào cần gọi phương thức stopThisThread(), vì có thể trên đường đến hàng đợi và tín hiệu dừng có thể đến trước (không mong muốn).

Mọi đề xuất?

+3

Lưu ý, 'wait' của bạn phải ở trong một 'while' vòng lặp, không phải là một' if', để bảo vệ chống lại wakeups giả mạo. –

+0

Để tất cả những người phụ thuộc vào gián đoạn: Chỉ muốn nói rằng cuộc sống không bao giờ đơn giản và có lần khi một cuộc gọi bị gián đoạn trên một "hoạt động" không gián đoạn và hoạt động đó hoàn toàn không có thông báo về gián đoạn. Điều này từ kinh nghiệm. Mặc dù vậy, tôi không biết cách nào tốt hơn ... Hãy xem cẩn thận về API: http://java.sun.com/javase/6/docs/api/index.html?java/lang/Thread .html kiểm tra phương thức ngắt() – Yaneeve

+0

@halfwarp: +1 và +1 cho câu trả lời của Stephen C. * "Sự kiện tử vong" * của bạn thực sự được gọi là * "thuốc độc" *: bạn chỉ đơn giản là enqueue một đối tượng đặc biệt (thuốc độc) có nghĩa là "ngăn chặn các chủ đề" một khi bạn dequeue nó. Bằng cách đó mọi thứ mà * "có thể đang trên đường đến hàng đợi" * [sic] vẫn được xử lý trước khi thuốc độc. – SyntaxT3rr0r

Trả lời

6

Phương thức tiếp cận DeathEvent (hoặc thường được gọi là "thuốc độc") hoạt động tốt nếu bạn cần hoàn thành tất cả công việc trên hàng đợi trước khi tắt. Vấn đề là điều này có thể mất nhiều thời gian.

Nếu bạn muốn dừng lại càng sớm càng tốt, tôi đề nghị bạn làm điều này

BlockingQueue<O> queue = ... 

... 

public void run() { 
    try { 
     // The following test is necessary to get fast interrupts. If 
     // it is replaced with 'true', the queue will be drained before 
     // the interrupt is noticed. (Thanks Tim) 
     while (!Thread.interrupted()) { 
      O obj = queue.take(); 
      doSomething(obj); 
     } 
    } catch (InterruptedException ex) { 
     // We are done. 
    } 
} 

Để dừng thread t rằng instantiated với phương thức run, chỉ cần gọi t.interrupt();.

Nếu bạn so sánh mã ở trên với các câu trả lời khác, bạn sẽ nhận thấy cách sử dụng số BlockingQueueThread.interrupt() đơn giản hóa giải pháp.

Tôi cũng tuyên bố rằng thêm một lá cờ stop là không cần thiết và trong bức tranh lớn, có thể gây hại. Một chuỗi công nhân được xử lý tốt nên tôn trọng một ngắt. Một ngắt bất ngờ đơn giản có nghĩa là nhân viên đang được chạy trong một ngữ cảnh mà lập trình viên ban đầu không lường trước được. Điều tốt nhất là nếu người lao động làm những gì nó được yêu cầu làm ... tức là nó nên dừng lại ... có hay không điều này phù hợp với quan điểm của lập trình viên ban đầu.

+0

@Stephen C: +1 ... Ngoài ra, trong JCIP, bạn nên khôi phục trạng thái bị gián đoạn bằng cách sử dụng * if (interrupted) {Thread.currenThread(). Interrupt()) } * xây dựng trong một khối * cuối cùng *. Hmmmm vì vậy sau khi tất cả có thể là cách tốt hơn là cả hai bỏ khi nhận được một gián đoạn bất ngờ và khôi phục lại tình trạng bị gián đoạn và sau đó * cũng * cho phép dừng "sạch" bằng cách sử dụng một số * stopThisThread() * phương pháp hoặc? – SyntaxT3rr0r

+0

@WizardOfOdds - Tôi không theo dõi bạn. 1) Không có điểm nào trong phương thức 'run' gọi là' interrupt() 'để khôi phục lại cờ. Sợi chỉ được gọi là sắp chết. 2) Không có gì bạn vừa nói giải thích tại sao dừng lại bằng ngắt là xấu hoặc tại sao cơ chế dừng "sạch" là tốt hơn. (Và một đề xuất JCIP là * không * một lời giải thích.) –

+0

@Bước C: trang JCIP 142, có "lời giải thích" của bạn. Btw Tôi đã không giả vờ để có bất kỳ lời giải thích và kết thúc bình luận của tôi với một dấu hỏi '?' trong trường hợp bạn không nhận thấy :) – SyntaxT3rr0r

0

Phương pháp tiếp cận 1 là tùy chọn ưu tiên.

Chỉ cần đặt trường biến động stop thành true và gọi interrupt() trên chuỗi đang chạy. Điều này sẽ buộc bất kỳ phương thức I/O nào chờ để trở về với một số InterruptedException (và nếu thư viện của bạn được viết chính xác thì điều này sẽ được xử lý một cách duyên dáng).

+2

Cử tri xuống có thể bình luận về những gì sai với câu trả lời này không? –

+1

Tôi vừa bỏ phiếu. Lý do của tôi là nó là không cần thiết để sử dụng một lĩnh vực dừng riêng biệt. Đồng thời là một chủ đề khó. Trong khi không có gì sai về mặt kỹ thuật với câu trả lời của bạn, nó cũng không phù hợp với thực hành tốt nhất và tôi nghĩ rằng sự quan tâm của cộng đồng là hạn chế việc trả lời các câu trả lời nhưng không phù hợp với thực tiễn tốt nhất. –

+0

@TimBender Tôi kính trọng không đồng ý. Sử dụng phương thức stop() trên đối tượng công nhân hàng đợi đơn giản là thiết lập trường boolean và ngắt chuỗi là do bất kỳ sự đồng thuận cộng đồng nào là "thực hành tốt nhất". – nsayer

0

Tôi nghĩ rằng hai trường hợp của bạn thực sự thể hiện hành vi tương tự. Đối với trường hợp thứ hai, xem xét Thread A thêm DeathEvent sau đó Thread B thêm một FooEvent. Khi công việc của bạn nhận được DeathEvent thì vẫn còn một FooEvent đằng sau nó, đó là cùng một kịch bản mà bạn mô tả trong Option 1, trừ khi bạn cố gắng xóa hàng đợi trước khi trở về, nhưng sau đó bạn chủ yếu giữ nguyên luồng còn lại, khi bạn đang cố gắng dừng lại.

Tôi đồng ý với bạn rằng tùy chọn đầu tiên là điều mong muốn hơn. Một giải pháp tiềm năng sẽ phụ thuộc vào cách xếp hàng của bạn.Nếu nó là một phần của công việc lớp chủ đề của bạn, bạn có thể có stopThisThread của bạn() phương pháp thiết lập một lá cờ đó sẽ trả về một giá trị thích hợp (hoặc ném ngoại lệ) từ ví dụ gọi enqueuing:

MyThread extends Thread{ 
    boolean running = true; 

    public void run(){ 
    while(running){ 
     try{ 
     //process queue... 
     }catch(InterruptedExcpetion e){ 
     ... 
     } 
    } 
    } 

    public void stopThisThread(){ 
    running = false; 
    interrupt(); 
    } 

    public boolean enqueue(Object o){ 
    if(!running){ 
     return false; 
     OR 
     throw new ThreadNotRunningException(); 
    } 
    queue.add(o); 
    return true; 
    } 
} 

Sau đó nó sẽ là trách nhiệm của đối tượng cố gắng để enqueue sự kiện để đối phó với nó một cách thích hợp, nhưng ít nhất nó sẽ biết rằng sự kiện này không có trong hàng đợi, và sẽ không được xử lý.

+0

Bạn tiếp cận sai, trừ khi bạn xác định biến đang chạy là biến động. Nó cũng không được khuyến khích để mở rộng Threads nhưng thay vì thực hiện Callable hoặc Runnable, vì vậy bạn sẽ không có chức năng gián đoạn trong phạm vi của bạn – Roman

0

Tôi thường đặt cờ trong lớp có Chủ đề trong đó và trong mã Chủ đề của tôi tôi sẽ làm. (LƯU Ý: Thay vì trong khi (đúng) tôi làm khi (cờ))

Sau đó, tạo phương thức trong lớp để đặt cờ thành sai;

private volatile bool flag = true; 

public void stopThread() 
{ 
    flag = false; 
} 

    public void run() { 
     Object obj; 
     while(flag) { 
      synchronized(objectsQueue) { 
       if(objectesQueue.isEmpty()) { 
        try { 
         objectesQueue.wait(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 

        obj = objectesQueue.poll(); 
       } 
      } 

      // Do something with the Object obj 
     } 
    } 
+0

Điều đó không nhìn chủ đề an toàn với tôi. Biến 'flag' đang được đọc và được viết trong các luồng khác nhau mà không có đồng bộ hóa thích hợp. –

+2

@Romain Hippeau: Tôi đã tạo cờ boolean * dễ bay hơi *, nếu không, như Stephen C đã chỉ ra, mã của bạn không được đồng bộ đúng cách. – SyntaxT3rr0r

+0

@WizardOfOdds - Cảm ơn chỉnh sửa –

1

Trong chuỗi trình đọc của bạn có điểm dừng biến boolean. Khi bạn muốn cho thread này dừng thiết lập thius thành true và ngắt thread. Trong chuỗi trình đọc khi an toàn (khi bạn không có đối tượng chưa được xử lý), hãy kiểm tra trạng thái của biến dừng và trả về vòng lặp nếu được đặt. như dưới đây.

public class readerThread extends Thread{ 
    private volitile boolean stop = false; 
    public void stopSoon(){ 
     stop = true; 
     this.interrupt(); 
    } 
    public void run() { 
     Object obj; 
     while(true) { 
      if(stop){ 
       return; 
      } 
      synchronized(objectsQueue) { 
      if(objectesQueue.isEmpty()) { 
       try { 
        objectesQueue.wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       if(stop){ 
        return; 
       }  
       obj = objectesQueue.poll(); 
       // Do something with the Object obj 
      } 
     } 
    } 


} 
public class OtherClass{ 
    ThreadReader reader; 
    private void start(){ 
      reader = ...; 
      reader.start(); 
    } 

    private void stop(){ 
      reader.stopSoon(); 
      reader.join();  // Wait for thread to stop if nessasery. 
    } 
} 
+0

Đây là cách tôi đã mã hóa một công nhân xếp hàng theo cách cổ điển. Sự khác biệt đáng kể giữa phương pháp này và câu trả lời được chấp nhận là phương pháp "thuốc độc" đảm bảo hàng đợi được xử lý hoàn toàn trước khi xuất cảnh, thay vì bị bỏ rơi. Cách tiếp cận nào chính xác hơn phụ thuộc vào không gian vấn đề. Bạn cũng có thể xem xét thêm một tham gia() sau ngắt(), làm cho khối stopSoon() cho đến khi luồng thực sự chết. – nsayer

1

Tại sao không sử dụng bộ lập lịch mà bạn có thể dừng lại khi cần? Trình lên lịch chuẩn hỗ trợ lập lịch lặp lại, cũng chờ chuỗi công nhân hoàn thành trước khi lên lịch lại một lần chạy mới.

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); 
service.scheduleWithFixedDelay(myThread, 1, 10, TimeUnit.SECONDS); 

mẫu này sẽ chạy chuỗi của bạn với thời gian trễ là 10 giây, khi khởi động xong, khởi động lại sau 10 giây. Và thay vì phải tái tạo lại bánh xe, bạn sẽ nhận được

service.shutdown() 

thời gian (thực)) không cần thiết nữa.

ScheduledExecutorService Javadoc

+1

'service.shutdown()' tương tự như cách tiếp cận thuốc độc theo nghĩa: không còn nhiệm vụ nào được chấp nhận nữa, nhiệm vụ thực thi sẽ tiếp tục chạy và các tác vụ được chấp nhận trước đó sẽ được thực hiện. Ngược lại, 'service.shutdownNow()' sẽ cố gắng ngừng thực hiện các tác vụ và các tác vụ được chấp nhận trước đó sẽ không được thực thi. – user454322

Các vấn đề liên quan