题
有时我看到的许多应用程序,如msn,windows media player等,是单一的实例应用程序(当户执行,同时应用程序正在运行一个新的应用程序的实例将不会创建)。
C#我的使用 Mutex
类于这个但我不知道如何做到这一点。
解决方案
如果我相信这个 文章, 通过:
具有的第一个实例,试图打开听插座上的localhost接口。如果它能够打开插座,这是假定的,这是第一个实例应用程序推出。如果不是,所作的假定是,一个实例,这个程序已在运行。新的实例必须通知的现有实例中,启动了尝试,然后退出。现有的实例需要结束后收到该通知和火灾事件的监听器处理行动。
注: 阿合 提到了在评论,即使用 InetAddress.getLocalHost()
可能是棘手的:
- 它不工作预计将在机环境,因为返回的地址取决于是否计算机网络接入。
解决方案是开放式连接InetAddress.getByAddress(new byte[] {127, 0, 0, 1})
;
可能是相关的 错误4435662.
- 我还发现 错误4665037 这份报告比预期的结果
getLocalHost
:返回的IP地址的机,对实际的结果:返回127.0.0.1
.
它是令人惊讶的是要有
getLocalHost
返回127.0.0.1
在Linux而不是在窗口。
或者您可以使用 ManagementFactory
对象。作为解释 在这里,:
的
getMonitoredVMs(int processPid)
方法接收作为参数的当前应用程序PID,并赶上应用程序名称从命令行,例如,应用程序的开始c:\java\app\test.jar
路径,那么价值变量"c:\\java\\app\\test.jar
".这种方式,我们会赶上只是程序上的名称线17的代码如下。
在那之后,我们搜索JVM为另一个进程具有相同的名称,如果我们发现和应用程序PID是不同的,这意味着是第二个应用程序的实例。
JNLP也提供了一个 SingleInstanceListener
其他提示
我在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;
}
如果应用程序。有一个GUI,用JWS启动它并使用 SingleInstanceService
。请参阅演示。用于(演示。和)示例代码的SingleInstanceService 。
是的,对于eclipse RCP eclipse单实例应用程序来说,这是一个非常好的答案 下面是我的代码
在application.java中
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;
}
我们使用的文件锁定用(抢排他锁在一个神奇的文件中的用户的应用程序的数据目录),但我们主要感兴趣于预防的多个实例是从以往运行。
如果你想要有第二个实例通过命令行args,等等。到第一个实例,那么使用一个插座连接在localhost将被杀害一石两鸟.总算法:
- 在启动,试图打开听者在港口XXXX在localhost
- 如果失败,打开一个作家,港口localhost和发送命令行args,然后关闭
- 否则,听取关于港口XXXXX on localhost.当收到命令行args,处理它们,如果程序启动与该命令行。
我找到了一个解决方案,有点卡通化的解释,但在大多数情况下仍然有效。它使用普通的旧锁文件创建东西,但是在一个完全不同的视图中:
http://javalandscape.blogspot.com/ 2008/07 /单实例从 - 你的-application.html
我认为这对那些拥有严格防火墙设置的人有帮助。
您可以使用JUnique库。它为运行单实例java应用程序提供支持,并且是开源的。
http://www.sauronsoftware.it/projects/junique/
JUnique库可用于防止用户同时运行 为同一Java应用程序添加更多实例。
JUnique实现了所有人之间共享的锁和通信通道 由同一用户启动的JVM实例。
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]));
}
}
}
在幕后,它在%USER_DATA%/。junique文件夹中创建文件锁,并在随机端口为每个允许在Java应用程序之间发送/接收消息的唯一appId创建服务器套接字。
在Windows上,您可以使用 launch4j 。
J2SE 5.0或更高版本支持的ManagementFactory类详细信息
但现在我使用J2SE 1.4,我发现这个 http://audiprimadhanty.wordpress.com/2008/06/30/ensuring-one-instance-of-application-running-at-one-time/ 但是我从不测试。你觉得怎么样?
您可以尝试使用Preferences API。它与平台无关。
一个更通用的方式的数量限制的实例是在单个机器,或者甚至整个网络,是采用多播插座。
使用一个插播,让你广播的消息的任何数额的应用程序实例,其中有些可以在物理上远程机器在企业网络。
以这种方式可以使许多类型的配置,以控制的东西喜欢
- 一个或许多情况下每台机器
- 一个或许多情况下,每个网络(例如:控制安装在一个客户的网站)
Java播支持通过 java.net 包 与 MulticastSocket & DatagramSocket 正的主要工具。
注意到:MulticastSocket的不能保证传送的数据包,因此应使用的一个工具上生成的多播插座喜欢 信。.信。 做 保证输送的所有数据。它是一个单一的罐子文件,与一个非常简单的。
信。已有一段时间,并具有一些令人印象深刻的用途的行业,例如它支持Boss的聚类机制做数据的广播给所有的实例,一个集群。
使用信。,限制的实例的数量的一个应用程序(在机器或一个网络,可以说:要数量的许可证的一个客户已经购买了)在概念上非常简单:
- 在启动时的程序,每个实例,试图加入一个名为基例如"我的巨大的应用程序的集团"。你将已经配置了该集团允许0,1或N成员
- 当小组的成员数大于已配置。你应该拒绝启动。
您可以打开内存映射文件,然后查看该文件是否已打开。如果它已经打开,你可以从main返回。
其他方法是使用锁文件(标准的unix练习)。另一种方法是在检查剪贴板中是否已存在某些内容后,在主要启动时将某些内容放入剪贴板。
否则,您可以在侦听模式(ServerSocket)中打开套接字。首先尝试连接到hte socket;如果你无法连接,那么打开一个serversocket。如果你连接,那么你知道另一个实例已经在运行。
因此,几乎任何系统资源都可用于了解应用程序正在运行。
BR, 〜A
我用座,并取决于如果申请是在客户的侧或服务器方的行为是有点不同:
- 客户面:如果一个实例,已经存在(我不能听着在一个特定的港口),我将通过应用程序参数和退出(可能需要执行一些行动以前的实例)如果不是我会开始应用程序。
- 服务器的一面:如果一个实例,已经存在,我将打印的消息和出口,如果不是我会开始应用程序。
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); } }
编辑:而不是使用这WatchService的方法,一个简单的1秒计时器螺纹可以用来检查如果indicatorFile.存在().删除它,然后把对应用程序toFront().
编辑:我想知道为什么这是downvoted.这是最好的解决方案我见过这么远。E.g。服务器插座种方法失败时,如果另一个应用程序发生在已经被听到港口。
刚刚下载的Microsoft Windows微软 套装软件 (或使用id)、开始,通过"国家",寻找线块,说"听",挑一个其边远的地址说您的计算机的名字,把那个放口进入你的新Socket()-解决方案。在我的执行情况,我可以产生故障的每一个时间。和它的 逻辑, 因为这是非常基础的方法。或者什么我没有得到关于如何实现这个吗?
请告诉我如果和如何我错了关于这个!
我看-这我问你到反驳的,如果可能的-那是开发人员都被建议使用一种方法产生的代码,无法在至少1的大约60000情况。如果这种图恰好是正确的,那么它绝对可以 不 是,一个解决方案提出,没有这个问题是downvoted和批评其量的代码。
缺点的座办法的比较:
- 失败如果错误的彩票(口数量)是选择。
- 失败在多用户的环境:只有一个用户可以运行的应用程序在相同的时间。(我的方法就必须略微改变,以创建文件(s)在用户树,但是这是微不足道的。)
- 失败如果防火墙的规则是过于严格。
- 使得可疑用户(其中我没有满足在野外)不知道什么把戏你来当你的文本编辑器,声称一个服务器插座。
我只是有一个很好的想法如何解决新的实例对现有实例Java通信问题的方式应该对每一个系统。因此,我掀起了这类中的大约两个小时。就像一个魅力:D
这是基于 罗伯特*'s文件锁定的方法(也可在此网页),这我已经使用。告诉已经运行实例的另一个实例,试图开始(但没有)...一个文件被创建和立即删除,第一个实例中使用的WatchService检测这个文件夹内容变化。我不能相信,显然,这是一个新的想法,因为根本问题。
这可以很容易地改变刚 创建 而不是删除文件然后信息可以把它正确的实例可以评估的,例如该命令行参数和适当的实例,然后可以执行的删除。就个人而言,我只需要知道什么时候恢复我的应用程序的窗口,并将它发送给前面。
例使用:
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();
}
}
}