Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/hadoop/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何实现单实例Java应用程序?_Java_Single Instance - Fatal编程技术网

如何实现单实例Java应用程序?

如何实现单实例Java应用程序?,java,single-instance,Java,Single Instance,有时我会看到许多应用程序,如msn、windows media player等,它们都是单实例应用程序(当用户在应用程序运行时执行时,不会创建新的应用程序实例) 在C#中,我使用了互斥对象类,但我不知道如何在Java中做到这一点。如果我相信这一点,通过以下方式: 让第一个实例尝试在localhost接口上打开侦听套接字。如果能够打开套接字,则假定这是要启动的应用程序的第一个实例。如果不是,则假定此应用程序的一个实例已在运行。新实例必须通知现有实例已尝试启动,然后退出。现有实例在收到通知后接管,并

有时我会看到许多应用程序,如msn、windows media player等,它们都是单实例应用程序(当用户在应用程序运行时执行时,不会创建新的应用程序实例)

在C#中,我使用了
互斥对象
类,但我不知道如何在Java中做到这一点。

如果我相信这一点,通过以下方式:

让第一个实例尝试在localhost接口上打开侦听套接字。如果能够打开套接字,则假定这是要启动的应用程序的第一个实例。如果不是,则假定此应用程序的一个实例已在运行。新实例必须通知现有实例已尝试启动,然后退出。现有实例在收到通知后接管,并向处理该操作的侦听器触发事件

注意:注释中提到使用
InetAddress.getLocalHost()
可能会很棘手:

  • 它在DHCP环境中无法正常工作,因为返回的地址取决于计算机是否具有网络访问权限。
    解决方案是打开与InetAddress.getByAddress(新字节[]{127,0,0,1})的连接
    可能与……有关
  • 我还发现哪些报告的结果超出了
    getLocalHost
    :返回机器的IP地址,而实际结果是:返回
    127.0.0.1
在Linux而不是windows上使用
getLocalHost
return
127.0.0.1
是令人惊讶的


或者您可以使用对象。如前所述:

getMonitoredVMs(int-processPid)
方法接收当前应用程序PID作为参数,并捕获从命令行调用的应用程序名称,例如,应用程序从
c:\java\app\test.jar
路径启动,然后值变量为“
c:\\java\\app\\test.jar
”。这样,我们将只在下面代码的第17行捕获应用程序名称。
之后,我们在JVM中搜索另一个同名进程,如果我们找到了它,并且应用程序PID不同,这意味着这是第二个应用程序实例

JNLP也提供了一个

如果我相信这一点,通过:

让第一个实例尝试在localhost接口上打开侦听套接字。如果能够打开套接字,则假定这是要启动的应用程序的第一个实例。如果不是,则假定此应用程序的一个实例已在运行。新实例必须通知现有实例已尝试启动,然后退出。现有实例在收到通知后接管,并向处理该操作的侦听器触发事件

注意:注释中提到使用
InetAddress.getLocalHost()
可能会很棘手:

  • 它在DHCP环境中无法正常工作,因为返回的地址取决于计算机是否具有网络访问权限。
    解决方案是打开与InetAddress.getByAddress(新字节[]{127,0,0,1})的连接
    可能与……有关
  • 我还发现哪些报告的结果超出了
    getLocalHost
    :返回机器的IP地址,而实际结果是:返回
    127.0.0.1
在Linux而不是windows上使用
getLocalHost
return
127.0.0.1
是令人惊讶的


或者您可以使用对象。如前所述:

getMonitoredVMs(int-processPid)
方法接收当前应用程序PID作为参数,并捕获从命令行调用的应用程序名称,例如,应用程序从
c:\java\app\test.jar
路径启动,然后值变量为“
c:\\java\\app\\test.jar
”。这样,我们将只在下面代码的第17行捕获应用程序名称。
之后,我们在JVM中搜索另一个同名进程,如果我们找到了它,并且应用程序PID不同,这意味着这是第二个应用程序实例


JNLP还提供了一个

我们使用文件锁定来实现这一点(在用户的应用程序数据目录中的魔法文件上获取独占锁),但我们主要关心的是防止多个实例运行

如果您试图让第二个实例通过命令行args等。。。首先,在localhost上使用套接字连接将是一举两得。通用算法:

  • 启动时,尝试在本地主机上的端口XXXX上打开侦听器
  • 如果失败,请在本地主机上打开该端口的写入程序并发送命令行args,然后关闭
  • 否则,请侦听本地主机上的端口XXXXX。当接收命令行参数时,将其处理为应用程序是使用该命令行启动的

我们为此使用文件锁定(在用户的应用程序数据目录中的魔法文件上获取独占锁定),但我们主要关心的是防止多个实例运行

如果您试图让第二个实例通过命令行args等。。。首先,在localhost上使用套接字连接将是一举两得。通用算法:

  • 启动时,尝试在本地主机上的端口XXXX上打开侦听器
  • 如果失败,请在本地主机上打开该端口的写入程序并发送命令行args,然后关闭
  • 否则,请侦听本地主机上的端口XXXXX。当接收命令行参数时,将其处理为应用程序是使用该命令行启动的

您可以打开内存映射文件,然后查看该文件是否已打开。如果它已经打开,您可以从main返回

其他方法是使用锁文件(标准unix实践)。另一种方法是在检查剪贴板中是否已经有内容后,在main启动时将内容放入剪贴板
private static boolean lockInstance(final String lockFile) {
    try {
        final File file = new File(lockFile);
        final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        final FileLock fileLock = randomAccessFile.getChannel().tryLock();
        if (fileLock != null) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    try {
                        fileLock.release();
                        randomAccessFile.close();
                        file.delete();
                    } catch (Exception e) {
                        log.error("Unable to remove lock file: " + lockFile, e);
                    }
                }
            });
            return true;
        }
    } catch (Exception e) {
        log.error("Unable to create and/or lock file: " + lockFile, e);
    }
    return false;
}
if(!isFileshipAlreadyRunning()){
        MessageDialog.openError(display.getActiveShell(), "Fileship already running", "Another instance of this application is already running.  Exiting.");
        return IApplication.EXIT_OK;
    } 


private static boolean isFileshipAlreadyRunning() {
    // socket concept is shown at http://www.rbgrn.net/content/43-java-single-application-instance
    // but this one is really great
    try {
        final File file = new File("FileshipReserved.txt");
        final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        final FileLock fileLock = randomAccessFile.getChannel().tryLock();
        if (fileLock != null) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    try {
                        fileLock.release();
                        randomAccessFile.close();
                        file.delete();
                    } catch (Exception e) {
                        //log.error("Unable to remove lock file: " + lockFile, e);
                    }
                }
            });
            return true;
        }
    } catch (Exception e) {
       // log.error("Unable to create and/or lock file: " + lockFile, e);
    }
    return false;
}
public class SingleInstance { public static final String LOCK = System.getProperty("user.home") + File.separator + "test.lock"; public static final String PIPE = System.getProperty("user.home") + File.separator + "test.pipe"; private static JFrame frame = null; public static void main(String[] args) { try { FileChannel lockChannel = new RandomAccessFile(LOCK, "rw").getChannel(); FileLock flk = null; try { flk = lockChannel.tryLock(); } catch(Throwable t) { t.printStackTrace(); } if (flk == null || !flk.isValid()) { System.out.println("alread running, leaving a message to pipe and quitting..."); FileChannel pipeChannel = null; try { pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel(); MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1); bb.put(0, (byte)1); bb.force(); } catch (Throwable t) { t.printStackTrace(); } finally { if (pipeChannel != null) { try { pipeChannel.close(); } catch (Throwable t) { t.printStackTrace(); } } } System.exit(0); } //We do not release the lock and close the channel here, // which will be done after the application crashes or closes normally. SwingUtilities.invokeLater( new Runnable() { public void run() { createAndShowGUI(); } } ); FileChannel pipeChannel = null; try { pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel(); MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1); while (true) { byte b = bb.get(0); if (b > 0) { bb.put(0, (byte)0); bb.force(); SwingUtilities.invokeLater( new Runnable() { public void run() { frame.setExtendedState(JFrame.NORMAL); frame.setAlwaysOnTop(true); frame.toFront(); frame.setAlwaysOnTop(false); } } ); } Thread.sleep(1000); } } catch (Throwable t) { t.printStackTrace(); } finally { if (pipeChannel != null) { try { pipeChannel.close(); } catch (Throwable t) { t.printStackTrace(); } } } } catch(Throwable t) { t.printStackTrace(); } } public static void createAndShowGUI() { frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(800, 650); frame.getContentPane().add(new JLabel("MAIN WINDOW", SwingConstants.CENTER), BorderLayout.CENTER); frame.setLocationRelativeTo(null); frame.setVisible(true); } }
public static void main(final String[] args) {

    // ENSURE SINGLE INSTANCE
    if (!SingleInstanceChecker.INSTANCE.isOnlyInstance(Main::otherInstanceTriedToLaunch, false)) {
        System.exit(0);
    }

    // launch rest of application here
    System.out.println("Application starts properly because it's the only instance.");
}

private static void otherInstanceTriedToLaunch() {
    // Restore your application window and bring it to front.
    // But make sure your situation is apt: This method could be called at *any* time.
    System.err.println("Deiconified because other instance tried to start.");
}
package yourpackagehere;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.nio.file.*;




/**
 * SingleInstanceChecker v[(2), 2016-04-22 08:00 UTC] by dreamspace-president.com
 * <p>
 * (file lock single instance solution by Robert https://stackoverflow.com/a/2002948/3500521)
 */
public enum SingleInstanceChecker {

    INSTANCE; // HAHA! The CONFUSION!


    final public static int POLLINTERVAL = 1000;
    final public static File LOCKFILE = new File("SINGLE_INSTANCE_LOCKFILE");
    final public static File DETECTFILE = new File("EXTRA_INSTANCE_DETECTFILE");


    private boolean hasBeenUsedAlready = false;


    private WatchService watchService = null;
    private RandomAccessFile randomAccessFileForLock = null;
    private FileLock fileLock = null;


    /**
     * CAN ONLY BE CALLED ONCE.
     * <p>
     * Assumes that the program will close if FALSE is returned: The other-instance-tries-to-launch listener is not
     * installed in that case.
     * <p>
     * Checks if another instance is already running (temp file lock / shutdownhook). Depending on the accessibility of
     * the temp file the return value will be true or false. This approach even works even if the virtual machine
     * process gets killed. On the next run, the program can even detect if it has shut down irregularly, because then
     * the file will still exist. (Thanks to Robert https://stackoverflow.com/a/2002948/3500521 for that solution!)
     * <p>
     * Additionally, the method checks if another instance tries to start. In a crappy way, because as awesome as Java
     * is, it lacks some fundamental features. Don't worry, it has only been 25 years, it'll sure come eventually.
     *
     * @param codeToRunIfOtherInstanceTriesToStart Can be null. If not null and another instance tries to start (which
     *                                             changes the detect-file), the code will be executed. Could be used to
     *                                             bring the current (=old=only) instance to front. If null, then the
     *                                             watcher will not be installed at all, nor will the trigger file be
     *                                             created. (Null means that you just don't want to make use of this
     *                                             half of the class' purpose, but then you would be better advised to
     *                                             just use the 24 line method by Robert.)
     *                                             <p>
     *                                             BE CAREFUL with the code: It will potentially be called until the
     *                                             very last moment of the program's existence, so if you e.g. have a
     *                                             shutdown procedure or a window that would be brought to front, check
     *                                             if the procedure has not been triggered yet or if the window still
     *                                             exists / hasn't been disposed of yet. Or edit this class to be more
     *                                             comfortable. This would e.g. allow you to remove some crappy
     *                                             comments. Attribution would be nice, though.
     * @param executeOnAWTEventDispatchThread      Convenience function. If false, the code will just be executed. If
     *                                             true, it will be detected if we're currently on that thread. If so,
     *                                             the code will just be executed. If not so, the code will be run via
     *                                             SwingUtilities.invokeLater().
     * @return if this is the only instance
     */
    public boolean isOnlyInstance(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        if (hasBeenUsedAlready) {
            throw new IllegalStateException("This class/method can only be used once, which kinda makes sense if you think about it.");
        }
        hasBeenUsedAlready = true;

        final boolean ret = canLockFileBeCreatedAndLocked();

        if (codeToRunIfOtherInstanceTriesToStart != null) {
            if (ret) {
                // Only if this is the only instance, it makes sense to install a watcher for additional instances.
                installOtherInstanceLaunchAttemptWatcher(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread);
            } else {
                // Only if this is NOT the only instance, it makes sense to create&delete the trigger file that will effect notification of the other instance.
                //
                // Regarding "codeToRunIfOtherInstanceTriesToStart != null":
                // While creation/deletion of the file concerns THE OTHER instance of the program,
                // making it dependent on the call made in THIS instance makes sense
                // because the code executed is probably the same.
                createAndDeleteOtherInstanceWatcherTriggerFile();
            }
        }

        optionallyInstallShutdownHookThatCleansEverythingUp();

        return ret;
    }


    private void createAndDeleteOtherInstanceWatcherTriggerFile() {

        try {
            final RandomAccessFile randomAccessFileForDetection = new RandomAccessFile(DETECTFILE, "rw");
            randomAccessFileForDetection.close();
            Files.deleteIfExists(DETECTFILE.toPath()); // File is created and then instantly deleted. Not a problem for the WatchService :)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private boolean canLockFileBeCreatedAndLocked() {

        try {
            randomAccessFileForLock = new RandomAccessFile(LOCKFILE, "rw");
            fileLock = randomAccessFileForLock.getChannel().tryLock();
            return fileLock != null;
        } catch (Exception e) {
            return false;
        }
    }


    private void installOtherInstanceLaunchAttemptWatcher(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        // PREPARE WATCHSERVICE AND STUFF
        try {
            watchService = FileSystems.getDefault().newWatchService();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        final File appFolder = new File("").getAbsoluteFile(); // points to current folder
        final Path appFolderWatchable = appFolder.toPath();


        // REGISTER CURRENT FOLDER FOR WATCHING FOR FILE DELETIONS
        try {
            appFolderWatchable.register(watchService, StandardWatchEventKinds.ENTRY_DELETE);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }


        // INSTALL WATCHER THAT LOOKS IF OUR detectFile SHOWS UP IN THE DIRECTORY CHANGES. IF THERE'S A CHANGE, ANOTHER INSTANCE TRIED TO START, SO NOTIFY THE CURRENT ONE OF THAT.
        final Thread t = new Thread(() -> watchForDirectoryChangesOnExtraThread(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread));
        t.setDaemon(true);
        t.setName("directory content change watcher");
        t.start();
    }


    private void optionallyInstallShutdownHookThatCleansEverythingUp() {

        if (fileLock == null && randomAccessFileForLock == null && watchService == null) {
            return;
        }

        final Thread shutdownHookThread = new Thread(() -> {
            try {
                if (fileLock != null) {
                    fileLock.release();
                }
                if (randomAccessFileForLock != null) {
                    randomAccessFileForLock.close();
                }
                Files.deleteIfExists(LOCKFILE.toPath());
            } catch (Exception ignore) {
            }
            if (watchService != null) {
                try {
                    watchService.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        Runtime.getRuntime().addShutdownHook(shutdownHookThread);
    }


    private void watchForDirectoryChangesOnExtraThread(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        while (true) { // To eternity and beyond! Until the universe shuts down. (Should be a volatile boolean, but this class only has absolutely required features.)

            try {
                Thread.sleep(POLLINTERVAL);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            final WatchKey wk;
            try {
                wk = watchService.poll();
            } catch (ClosedWatchServiceException e) {
                // This situation would be normal if the watcher has been closed, but our application never does that.
                e.printStackTrace();
                return;
            }

            if (wk == null || !wk.isValid()) {
                continue;
            }


            for (WatchEvent<?> we : wk.pollEvents()) {

                final WatchEvent.Kind<?> kind = we.kind();
                if (kind == StandardWatchEventKinds.OVERFLOW) {
                    System.err.println("OVERFLOW of directory change events!");
                    continue;
                }


                final WatchEvent<Path> watchEvent = (WatchEvent<Path>) we;
                final File file = watchEvent.context().toFile();


                if (file.equals(DETECTFILE)) {

                    if (!executeOnAWTEventDispatchThread || SwingUtilities.isEventDispatchThread()) {
                        codeToRunIfOtherInstanceTriesToStart.run();
                    } else {
                        SwingUtilities.invokeLater(codeToRunIfOtherInstanceTriesToStart);
                    }

                    break;

                } else {
                    System.err.println("THIS IS THE FILE THAT WAS DELETED: " + file);
                }

            }

            wk.reset();
        }
    }

}
public static void main(String[] args) {
    String appId = "myapplicationid";
    boolean alreadyRunning;
    try {
        JUnique.acquireLock(appId, new MessageHandler() {
            public String handle(String message) {
                // A brand new argument received! Handle it!
                return null;
            }
        });
        alreadyRunning = false;
    } catch (AlreadyLockedException e) {
        alreadyRunning = true;
    }
    if (!alreadyRunning) {
        // Start sequence here
    } else {
        for (int i = 0; i < args.length; i++) {
            JUnique.sendMessage(appId, args[0]));
        }
    }
}
import tk.pratanumandal.unique4j.Unique4j;
import tk.pratanumandal.unique4j.exception.Unique4jException;

public class Unique4jDemo {

    // unique application ID
    public static String APP_ID = "tk.pratanumandal.unique4j-mlsdvo-20191511-#j.6";

    public static void main(String[] args) throws Unique4jException, InterruptedException {

        // create unique instance
        Unique4j unique = new Unique4j(APP_ID) {
            @Override
            public void receiveMessage(String message) {
                // display received message from subsequent instance
                System.out.println(message);
            }

            @Override
            public String sendMessage() {
                // send message to first instance
                return "Hello World!";
            }
        };

        // try to obtain lock
        boolean lockFlag = unique.acquireLock();

        // sleep the main thread for 30 seconds to simulate long running tasks
        Thread.sleep(30000);

        // try to free the lock before exiting program
        boolean lockFreeFlag = unique.freeLock();

    }

}