2017-02-05 14 views
6

Tôi có một phương pháp dưới đây mà tôi muốn thực hiện trên dưới điều kiện:Làm thế nào để đảm bảo rằng phương thức này chỉ được thực thi một lần và từ một luồng?

  • Phương pháp này nên được thực hiện một lần duy nhất. Và một khi nó được thực hiện, nó không thể được thực hiện một lần nữa vì vậy nếu bất cứ ai cố gắng thực hiện một lần nữa, nó sẽ trở lại bằng cách đăng nhập một số thông báo lỗi hữu ích already executed hoặc bất cứ điều gì hữu ích.
  • Và nó chỉ được thực hiện bởi một chuỗi. Vì vậy, nếu nhiều chủ đề được gọi phương thức dưới đây, sau đó nó nên được gọi bằng chỉ một sợi và các chủ đề khác nên chờ đợi để khởi tạo để hoàn thành?

Dưới đây là phương pháp của tôi:

public void initialize() { 
    List<Metadata> metadata = getMetadata(true); 
    List<Process> process = getProcess(); 
    if (!metadata.isEmpty() && !process.isEmpty()) { 
     Manager.setAllMetadata(metadata, process); 
    } 
    startBackgroundThread(); 
    } 

Đây có phải là có thể làm gì? Tôi đang làm việc với Java 7.

+0

Nếu bạn muốn chắc chắn rằng một đoạn mã được thực hiện đúng một lần, hơn tôi nghĩ rằng việc đưa nó trong initializer tĩnh lớp một của Enum là càng gần càng tốt được. JVM đảm bảo rằng điều này sẽ được gọi tối đa một lần cho mỗi trình nạp lớp. Nhưng tốt hơn thế, tôi không nghĩ rằng có một cách rõ ràng để đảm bảo như vậy. Có thể nếu bạn đã cung cấp thêm thông tin về trường hợp sử dụng của mình? – korolar

+0

Tôi có phương pháp này trong một lớp học của tôi mà khởi tạo tất cả các siêu dữ liệu của chúng tôi và một khi khởi tạo xong, sau đó chỉ tôi muốn di chuyển về phía trước trong ứng dụng của tôi. – user1950349

+1

Sẽ không đặt nó trong bộ khởi tạo tĩnh của lớp này là đủ? – korolar

Trả lời

6

@ Giải pháp của ShayHaned sử dụng khóa. Bạn có thể làm cho nó hiệu quả hơn thông qua AtomicBoolean như:

AtomicBoolean wasRun = new AtomicBoolean(false); 
CountDownLatch initCompleteLatch = new CountDownLatch(1); 

public void initialize() { 
    if (!wasRun.getAndSet(true)) { 
     List<Metadata> metadata = getMetadata(true); 
     List<Process> process = getProcess(); 
     if (!metadata.isEmpty() && !process.isEmpty()) { 
      Manager.setAllMetadata(metadata, process); 
     } 
     startBackgroundThread(); 
     initCompleteLatch.countDown(); 
    } else { 
     log.info("Waiting to ensure initialize is done."); 
     initCompleteLatch.await(); 
     log.warn("I was already run"); 
    } 
} 

Trên đây giả sử bạn không cần phải chờ đợi cho công việc trong startBackgroundThread để hoàn thành. Nếu bạn làm thế, giải pháp trở thành:

AtomicBoolean wasRun = new AtomicBoolean(false); 
CountDownLatch initCompleteLatch = new CountDownLatch(1); 

public void initialize() { 
    if (!wasRun.getAndSet(true)) { 
     List<Metadata> metadata = getMetadata(true); 
     List<Process> process = getProcess(); 
     if (!metadata.isEmpty() && !process.isEmpty()) { 
      Manager.setAllMetadata(metadata, process); 
     } 
     // Pass the latch to startBackgroundThread so it can 
     // call countDown on it when it's done. 
     startBackgroundThread(initCompleteLatch); 
    } else { 
     log.info("Waiting to ensure initialize is done."); 
     initCompleteLatch.await(); 
     log.warn("I was already run"); 
    } 
} 

Lý do làm việc này là AtomicBoolean.getAndSet(true) sẽ, trong một hoạt động nguyên tử, trả về giá trị mà trước đó đã đặt ra cho và làm cho giá trị mới được true. Vì vậy, các chủ đề đầu tiên để có được phương pháp của bạn sẽ nhận được false trả lại (kể từ khi biến được khởi tạo sai) và nó sẽ, nguyên tử, đặt nó thành sự thật. Vì luồng đầu tiên đó đã trả về false, nó sẽ lấy nhánh đầu tiên trong câu lệnh if và khởi tạo của bạn sẽ xảy ra. Bất kỳ cuộc gọi nào khác cũng sẽ thấy rằng wasRun.getAndSet trả về true vì chuỗi đầu tiên được đặt thành đúng nên chúng sẽ lấy nhánh thứ 2 và bạn sẽ chỉ nhận được thông báo tường trình bạn muốn.

CountDownLatch được khởi tạo thành 1 vì vậy tất cả các chủ đề khác với lệnh gọi đầu tiên await trên đó. Họ sẽ chặn cho đến khi chủ đề đầu tiên gọi countDown mà sẽ thiết lập số để 0 phát hành tất cả các chủ đề chờ đợi.

+0

Bạn có thể vui lòng thêm một số giải thích để tôi có thể hiểu không? Ngoài ra nó sẽ chăm sóc cả hai điều kiện của tôi? Nếu bạn có thể giải thích căn bản về điều đó, thì nó sẽ giúp tôi hiểu. Tôi đã làm một số nghiên cứu và tôi nghĩ rằng tôi có thể phải sử dụng 'CountDownLatch' cùng với' AtomicBoolean' như bạn đề xuất ở đây? – user1950349

+0

Đã thêm giải thích. –

+1

Tôi không nghĩ rằng câu trả lời này là hoàn toàn thỏa mãn các yêu cầu. @ user1950349 yêu cầu thứ hai không được đáp ứng. Ở đây, sẽ không phải tất cả các chủ đề sau khi người đầu tiên sẽ nghĩ rằng phương pháp này _was_ chạy; nhưng phương thức có thể _still đang chạy_. Tất cả các chủ đề sau khi đầu tiên phải chờ đợi, đó là nơi 'CountdownLatch' sẽ có hiệu quả. [Post] này (http://stackoverflow.com/questions/289434/how-to-make-a-java-thread-wait-for-another-threads-output) đề cập đến cách tiếp cận đó - câu trả lời của @ pdeva nói riêng. – Keith

1

• Và nó chỉ được thực hiện bởi chỉ một chuỗi. Vì vậy, nếu nhiều chủ đề được gọi phương thức dưới đây, sau đó nó nên được gọi bằng chỉ một sợi và các chủ đề khác nên chờ đợi để khởi tạo để hoàn thành?

public static final Object singleThreadLock = new Object(); 

public void initialize() 
{ 
    synchronized(singleThreadLock) 
    { 

     List<Metadata> metadata = getMetadata(true); 
     List<Process> process = getProcess(); 
     if (!metadata.isEmpty() && !process.isEmpty()) 
     { 
      Manager.setAllMetadata(metadata, process); 
     } 
     startBackgroundThread(); 
    } 
    } 

Những dòng mã BẢO LÃNH rằng khởi tạo() sẽ được gọi là một lần duy nhất cho mỗi thread, và kể từ singleThreadLock được khai báo tĩnh, sau đó JVM hiện sinh ra bạn sẽ không bao giờ cho phép bất kỳ chủ đề khác để truy cập vào các khóa cho đến khi khối bên trong đồng bộ được thực hiện hoàn toàn. Ngoài ra, hãy tránh xa việc cố gắng đồng bộ hóa (điều này), vì các tuyên bố như vậy có thể dẫn đến các vấn đề tương tranh nghiêm trọng.

+0

Không có gì ngăn chặn nhiều chủ đề từ _eventual_ gọi hàm này. Đọc yêu cầu thứ hai của anh ta. Một số loại biến 'wasRun' cần được đặt thành true sau khi' startBackgroundThread() 'được gọi, và' wasRun' sau đó sẽ được đánh giá ở đầu phần quan trọng. – Keith

2

bạn có thể tạo cờ tĩnh cho phương thức của mình. Phương thức này sẽ chỉ được thay đổi khi phương thức được gọi. Ý tưởng đằng sau việc sử dụng cờ tĩnh là một thực tế là nó không thuộc về instance nó thuộc về class nghĩa là tất cả các thread được tạo ra từ cùng một lớp sẽ có quyền truy cập vào cùng một giá trị của cờ, một khi giá trị boolean của flag được thay đổi sau lần gọi đầu tiên phương pháp tất cả các chủ đề khác sẽ được bỏ qua bởi nếu người khác có điều kiện tuyên bố.

static boolen flag; 
public void initialize() { 
if (flag) 
{// return from here or some message you want to generate 
}else{ 
    List<Metadata> metadata = getMetadata(true); 
    List<Process> process = getProcess(); 
    if (!metadata.isEmpty() && !process.isEmpty()) { 
     Manager.setAllMetadata(metadata, process); 
    } 
     flag = true; 
    startBackgroundThread();  }} 

Tôi hy vọng điều này giải quyết truy vấn của bạn

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