2009-05-28 39 views
26

Tôi cần ngăn người dùng khởi động ứng dụng Java của tôi (ứng dụng WebStart Swing) nhiều lần. Vì vậy, nếu ứng dụng đang chạy, bạn không thể khởi động lại hoặc hiển thị cảnh báo/đóng lại.Làm thế nào để cho phép chạy chỉ một thể hiện của một chương trình Java tại một thời điểm?

Có cách nào thuận tiện để đạt được điều này không? Tôi nghĩ về việc chặn một cổng hoặc viết sth vào một tập tin. Nhưng hy vọng bạn có thể truy cập một số thuộc tính hệ thống hoặc JVM?

btw. nền tảng đích là Windows XP với Java 1.5

+0

Hầu hết các giải pháp đơn giản nhất :-) java.nio.file khóa thấy hoàn dụ http: // stackoverflow. com/a/20015771/185022 –

Trả lời

35

Tôi nghĩ đề xuất của bạn về việc mở một cổng để lắng nghe khi bạn khởi động ứng dụng là ý tưởng tốt nhất.

Rất dễ thực hiện và bạn không cần phải lo lắng về việc làm sạch nó khi bạn đóng ứng dụng của mình. Ví dụ, nếu bạn viết vào một tập tin nhưng một người nào đó sau đó giết chết các quy trình bằng cách sử dụng Task Manager, tập tin sẽ không bị xóa.

Ngoài ra, nếu tôi nhớ chính xác không có cách nào dễ dàng để lấy PID của một tiến trình Java từ bên trong JVM, do đó đừng thử và xây dựng một giải pháp bằng cách sử dụng các PID.

Something như thế này nên làm như lừa:

private static final int PORT = 9999; 
private static ServerSocket socket;  

private static void checkIfRunning() { 
    try { 
    //Bind to localhost adapter with a zero connection queue 
    socket = new ServerSocket(PORT,0,InetAddress.getByAddress(new byte[] {127,0,0,1})); 
    } 
    catch (BindException e) { 
    System.err.println("Already running."); 
    System.exit(1); 
    } 
    catch (IOException e) { 
    System.err.println("Unexpected error."); 
    e.printStackTrace(); 
    System.exit(2); 
    } 
} 

mẫu mã này rõ ràng liên kết với 127.0.0.1 mà nên tránh bất kỳ cảnh báo tường lửa, như bất kỳ giao thông trên địa chỉ này phải từ hệ thống địa phương.

Khi chọn một cổng cố gắng tránh một đề cập trong list of Well Known Ports. Bạn nên lý tưởng làm cho cổng được sử dụng cấu hình trong một tập tin hoặc thông qua một chuyển đổi dòng lệnh trong trường hợp xung đột.

+0

Ý tưởng hay! Có thể có bất kỳ sự cố bảo mật nào với SecurityManager rất nghiêm ngặt không? – guerda

+1

Điều này có gây ra bất kỳ cảnh báo tường lửa nào không? Làm thế nào để chọn một cổng mà chúng tôi biết là không được sử dụng bởi các chương trình khác? – Erwin

+0

Như Erwin chỉ ra, tôi tin rằng điều này sẽ gây ra cảnh báo tường lửa. Tôi hoàn toàn không thể đồng ý với cách tiếp cận này. Phải có một cách thanh lịch hơn. Jon Skeet đâu rồi? –

0

Bạn có thể sử dụng sổ đăng ký, mặc dù điều này hết sức đánh bại mục đích sử dụng ngôn ngữ cấp cao như java. Ít nhất nền tảng mục tiêu của bạn là windows = D

+0

Nó cũng gặp vấn đề tương tự như tệp khóa: nếu chương trình bị chết, làm cách nào để bạn biết khi nào nên phá khóa? – simpleuser

1

Chúng tôi cũng làm như vậy trong C++ bằng cách tạo đối tượng muten kernal và tìm kiếm nó lúc khởi động. Các ưu điểm cũng giống như sử dụng một socket, tức là khi quá trình chết/treo/thoát/bị giết, đối tượng mutex được làm sạch bởi hạt nhân.

Tôi không phải là lập trình viên Java, vì vậy tôi không chắc liệu bạn có thể làm cùng một loại điều trong Java không?

26

Khi câu hỏi nói rằng WebStart đang được sử dụng, giải pháp hiển nhiên là sử dụng javax.jnlp.SingleInstanceService.

Dịch vụ này khả dụng ở 1.5. Lưu ý rằng 1.5 hiện tại hầu hết là thông qua giai đoạn End Of Service Life. Nhận với Java SE 6!

+0

Giải pháp hoàn hảo, cảm ơn bạn. –

3

Tôi nghĩ rằng ý tưởng tốt hơn là sử dụng khóa tệp (một ý tưởng hoàn toàn cũ :)). Kể từ khi Java 1.4 một thư viện I/O mới được giới thiệu, cho phép khóa tập tin.

Sau khi ứng dụng bắt đầu, nó cố gắng lấy khóa trên một tệp (hoặc tạo nó nếu không tồn tại), khi ứng dụng thoát khỏi khóa sẽ bị xóa. Nếu ứng dụng không thể có khóa, nó sẽ thoát.

Ví dụ về cách thực hiện khóa tệp là ví dụ trong Java Developers Almanac.

Nếu bạn muốn sử dụng khóa tệp trong ứng dụng Java Web Start hoặc một applet, bạn cần phải hát ứng dụng hoặc applet.

+0

Khóa tệp sẽ không hoạt động do Taskmanager hoặc Linux giết các lệnh không giải phóng tệp khóa – JPM

-1

Tôi đã xem rất nhiều câu hỏi này và tôi đang tìm cách giải quyết cùng một vấn đề theo cách độc lập trên nền tảng không có cơ hội va chạm với tường lửa hoặc vào công cụ ổ cắm.

Vì vậy, đây là những gì tôi đã làm:

import java.io.File; 
import java.io.IOException; 

/** 
* This static class is in charge of file-locking the program 
* so no more than one instance can be run at the same time. 
* @author nirei 
*/ 
public class SingleInstanceLock { 

    private static final String LOCK_FILEPATH = System.getProperty("java.io.tmpdir") + File.separator + "lector.lock"; 
    private static final File lock = new File(LOCK_FILEPATH); 
    private static boolean locked = false; 

    private SingleInstanceLock() {} 

    /** 
    * Creates the lock file if it's not present and requests its deletion on 
    * program termination or informs that the program is already running if 
    * that's the case. 
    * @return true - if the operation was succesful or if the program already has the lock.<br> 
    * false - if the program is already running 
    * @throws IOException if the lock file cannot be created. 
    */ 
    public static boolean lock() throws IOException { 
     if(locked) return true; 

     if(lock.exists()) return false; 

     lock.createNewFile(); 
     lock.deleteOnExit(); 
     locked = true; 
     return true; 
    } 
} 

Sử dụng System.getProperty ("java.io.tmpdir") cho đường dẫn lockfile đảm bảo rằng bạn sẽ luôn luôn tạo khóa của bạn trên cùng một vị trí.

Sau đó, từ chương trình của bạn, bạn chỉ cần gọi một cái gì đó như:

blah blah main(blah blah blah) { 
    try() { 
     if(!SingleInstanceLock.lock()) { 
      System.out.println("The program is already running"); 
      System.exit(0); 
     } 
    } catch (IOException e) { 
     System.err.println("Couldn't create lock file or w/e"); 
     System.exit(1); 
    } 
} 

Và nào đó cho tôi. Bây giờ, nếu bạn giết chương trình, nó sẽ không xóa tệp khóa nhưng bạn có thể giải quyết điều này bằng cách viết PID của chương trình vào tệp khóa và làm cho phương thức lock() kiểm tra xem quá trình đó đã chạy chưa. Điều này được để lại như một sự đảm bảo cho bất cứ ai quan tâm. :)

0

Hãy thử JUnique:

String appId = "com.example.win.run.main"; 
boolean alreadyRunning; 
try { 
    JUnique.acquireLock(appId); 
    alreadyRunning = false; 
} catch (AlreadyLockedException e) { 
    alreadyRunning = true; 
} 
if (alreadyRunning) { 
    Sysout("An Instance of this app is already running"); 
    System.exit(1); 
} 
Các vấn đề liên quan