2012-04-02 35 views
13

Tôi có một chương trình java sử dụng 20 chủ đề. Mỗi người trong số họ viết kết quả của họ trong một tập tin gọi là output.txt.Chủ đề và tập tin viết

Tôi luôn nhận được một số dòng khác nhau trong output.txt.

Có thể có vấn đề với việc đồng bộ hóa chuỗi không? Có cách nào để xử lý việc này không?

+0

Vâng, điều này không rõ ràng về cách triển khai của bạn. Như trường hợp thử nghiệm đơn giản của tôi cho thấy, tôi nhận được dòng đầu ra liên tục với FileWriter với 20 luồng. Có thể cần phải thêm một số chi tiết triển khai. Xem câu trả lời của tôi. – FaithReaper

Trả lời

26

có thể là vấn đề đồng bộ hóa chuỗi không?

Có.

Có cách để xử lý việc này?

Có, đảm bảo rằng việc ghi được tuần tự hóa bằng cách đồng bộ hóa trên một mutex có liên quan. Hoặc luân phiên, chỉ có một chuỗi thực sự xuất ra tệp và có tất cả các chuỗi khác chỉ đơn giản là xếp hàng văn bản được ghi vào hàng đợi mà chuỗi văn bản rút ra. (Bằng cách đó 20 chủ đề chính không chặn trên I/O).

Re mutex: Ví dụ, nếu tất cả chúng sử dụng cùng một FileWriter dụ (hoặc bất kỳ), mà tôi sẽ đề cập là fw, sau đó họ có thể sử dụng nó như một mutex:

synchronized (fw) { 
    fw.write(...); 
} 

Nếu họ từng sử dụng riêng của họ FileWriter hoặc bất cứ điều gì, tìm cái gì khác họ đều có chung là mutex.

Nhưng một lần nữa, việc tạo chuỗi I/O thay mặt cho những người khác cũng có thể là một cách hay để đi.

+0

Bạn có thể vui lòng cụ thể hơn về cách tiếp cận tốt nhất cho phương pháp tiếp cận một luồng nhà văn không? Ví dụ. có thể sử dụng [Trình xử lý một luồng đơn] (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newSingleThreadExecutor--) và chỉ gửi các tác vụ đến đó 'Executor '? – Roland

9

Tôi khuyên bạn nên sắp xếp theo cách này: Một người tiêu dùng theo chủ đề sẽ tiêu thụ tất cả dữ liệu và ghi dữ liệu đó vào tệp. Tất cả các luồng công nhân sẽ tạo ra dữ liệu cho luồng người tiêu dùng theo cách đồng bộ. Hoặc với nhiều chủ đề tập tin văn bản, bạn có thể sử dụng một số thực hiện mutex hoặc khóa.

+1

+1 Tôi vừa thêm đề xuất này vào câu trả lời của tôi khi bạn đang viết thư cho bạn. :-) –

1

Bạn nên sử dụng đồng bộ hóa trong trường hợp này. Hãy tưởng tượng rằng 2 chủ đề (t1 và t2) mở tập tin cùng một lúc và bắt đầu viết cho nó. Các thay đổi được thực hiện bởi luồng đầu tiên được ghi đè bởi chuỗi thứ hai vì luồng thứ hai là luồng cuối cùng để lưu các thay đổi vào tệp. Khi một chuỗi t1 đang ghi vào tệp, t2 phải chờ cho đến khi t1 hoàn thành nhiệm vụ của nó trước khi nó có thể mở nó.

2

Nếu bạn muốn bất kỳ semblance hiệu suất và dễ quản lý, đi với hàng đợi người tiêu dùng sản xuất và chỉ là một tập tin-nhà văn, theo đề nghị của Alex và những người khác. Cho phép tất cả các chủ đề tại tập tin với một mutex chỉ là lộn xộn - mọi sự chậm trễ đĩa được chuyển trực tiếp vào chức năng ứng dụng chính của bạn, (với sự tranh cãi thêm). Điều này đặc biệt khó chịu với các ổ đĩa mạng chậm có xu hướng biến mất mà không cần cảnh báo.

1

Nếu bạn có thể giữ tập tin của bạn như một FileOutputStream bạn có thể khóa nó như thế này:

FileOutputStream file = ... 
.... 
// Thread safe version. 
void write(byte[] bytes) { 
    try { 
    boolean written = false; 
    do { 
     try { 
     // Lock it! 
     FileLock lock = file.getChannel().lock(); 
     try { 
      // Write the bytes. 
      file.write(bytes); 
      written = true; 
     } finally { 
      // Release the lock. 
      lock.release(); 
     } 
     } catch (OverlappingFileLockException ofle) { 
     try { 
      // Wait a bit 
      Thread.sleep(0); 
     } catch (InterruptedException ex) { 
      throw new InterruptedIOException ("Interrupted waiting for a file lock."); 
     } 
     } 
    } while (!written); 
    } catch (IOException ex) { 
    log.warn("Failed to lock " + fileName, ex); 
    } 
} 
+0

Điều này hoàn toàn dư thừa vì tồn tại 'đồng bộ'. – EJP

+0

@EJP - xem [FileLock] (https://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileLock.html) - * các khóa được giữ trên một tệp sẽ hiển thị cho tất cả các chương trình có quyền truy cập vào tệp, bất kể ngôn ngữ mà các chương trình đó được viết * - vì vậy, về mặt lý thuyết, chúng * nên * tốt hơn so với 'được đồng bộ hóa' nhưng thường thì không. – OldCurmudgeon

+0

Điều đó không làm cho nó không thừa, hoặc 'tốt hơn' đồng bộ'. Không có gì về các quy trình khác hoặc các ngôn ngữ khác trong câu hỏi. – EJP

0

Vâng, không có bất kỳ chi tiết thực hiện, rất khó để biết, nhưng như trường hợp thử nghiệm của tôi cho thấy, tôi luôn nhận được 220 dòng đầu ra, tức là số dòng liên tục, với FileWriter. Lưu ý rằng không có synchronized nào được sử dụng tại đây.

import java.io.File; 
import java.io.FileWriter; 
import java.io.IOException; 
/** 
* Working example of synchonous, competitive writing to the same file. 
* @author WesternGun 
* 
*/ 
public class ThreadCompete implements Runnable { 
    private FileWriter writer; 
    private int status; 
    private int counter; 
    private boolean stop; 
    private String name; 


    public ThreadCompete(String name) { 
     this.name = name; 
     status = 0; 
     stop = false; 
     // just open the file without appending, to clear content 
     try { 
      writer = new FileWriter(new File("test.txt"), true); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

    } 


    public static void main(String[] args) { 

     for (int i=0; i<20; i++) { 
      new Thread(new ThreadCompete("Thread" + i)).start(); 
     } 
    } 

    private int generateRandom(int range) { 
     return (int) (Math.random() * range); 
    } 

    @Override 
    public void run() { 
     while (!stop) { 
      try { 
       writer = new FileWriter(new File("test.txt"), true); 
       if (status == 0) { 
        writer.write(this.name + ": Begin: " + counter); 
        writer.write(System.lineSeparator()); 
        status ++; 
       } else if (status == 1) { 
        writer.write(this.name + ": Now we have " + counter + " books!"); 
        writer.write(System.lineSeparator()); 
        counter++; 
        if (counter > 8) { 
         status = 2; 
        } 

       } else if (status == 2) { 
        writer.write(this.name + ": End. " + counter); 
        writer.write(System.lineSeparator()); 
        stop = true; 
       } 
       writer.flush(); 
       writer.close(); 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

Theo tôi được biết (và thử nghiệm), có hai giai đoạn trong quá trình này:

  • tất cả các chủ đề trong hồ bơi tất cả tạo ra và bắt đầu, sẵn sàng để lấy các tập tin;
  • một trong số họ lấy nó, và Tôi đoán nó sau đó khóa nội bộ nó, ngăn chặn các chủ đề khác để truy cập, bởi vì tôi không bao giờ nhìn thấy một dòng kết hợp nội dung đến từ hai chủ đề. Vì vậy, khi một thread được viết, những người khác đang chờ đợi cho đến khi nó hoàn thành dòng, và rất có thể, phát hành tập tin. Vì vậy, sẽ không có điều kiện chủng tộc nào xảy ra.
  • nhanh nhất trong số những người khác lấy tệp và bắt đầu viết.

Vâng, nó giống như một đám đông chờ đợi bên ngoài phòng tắm, mà không cần xếp hàng .....

Vì vậy, nếu thực hiện của bạn là khác nhau, hiển thị mã và chúng tôi có thể giúp để phá vỡ nó xuống.

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