@ 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.
Nguồn
2017-02-05 02:52:17
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
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
Sẽ không đặt nó trong bộ khởi tạo tĩnh của lớp này là đủ? – korolar