แก้ไข : แทนที่จะใช้แนวทาง WatchService นี้สามารถใช้เธรดตัวจับเวลา 1 วินาทีแบบธรรมดาเพื่อตรวจสอบว่า indicatorFile.exists () หรือไม่ ลบออกจากนั้นนำแอปพลิเคชันไปที่ Front ()
แก้ไข : ฉันอยากทราบว่าเหตุใดจึงถูกลดคะแนน เป็นทางออกที่ดีที่สุดที่ฉันเคยเห็นมา เช่นวิธีการซ็อกเก็ตเซิร์ฟเวอร์ล้มเหลวหากมีแอพพลิเคชั่นอื่นกำลังฟังพอร์ตอยู่แล้ว
เพียงดาวน์โหลด Microsoft Windows Sysinternals TCPView (หรือใช้ netstat) เริ่มต้นโดยจัดเรียงตาม "สถานะ" มองหาบล็อกบรรทัดที่ระบุว่า "LISTENING" เลือกที่อยู่ระยะไกลที่ระบุชื่อคอมพิวเตอร์ของคุณใส่พอร์ตนั้นลงในซ็อกเก็ตใหม่ของคุณ ()-วิธีการแก้. ในการนำไปใช้งานของฉันฉันสามารถสร้างความล้มเหลวได้ทุกครั้ง และมันก็มีเหตุผลเพราะมันเป็นรากฐานของแนวทาง หรือฉันไม่ได้รับอะไรเกี่ยวกับวิธีการนำไปใช้?
โปรดแจ้งให้ฉันทราบว่าฉันผิดเกี่ยวกับเรื่องนี้อย่างไร!
มุมมองของฉัน - ซึ่งฉันขอให้คุณพิสูจน์ว่าเป็นไปได้ - คือการที่นักพัฒนาได้รับคำแนะนำให้ใช้แนวทางในรหัสการผลิตที่จะล้มเหลวอย่างน้อย 1 ในประมาณ 60000 กรณี และหากมุมมองนี้เกิดขึ้นถูกต้องก็เป็นไปไม่ได้เลยที่วิธีการแก้ปัญหาที่นำเสนอซึ่งไม่มีปัญหานี้จะถูกลดลงและถูกวิพากษ์วิจารณ์ถึงจำนวนรหัส
ข้อเสียของการเปรียบเทียบวิธีซ็อกเก็ต:
- ล้มเหลวหากเลือกตั๋วลอตเตอรีผิด (หมายเลขพอร์ต)
- ล้มเหลวในสภาพแวดล้อมที่มีผู้ใช้หลายคน: ผู้ใช้เพียงคนเดียวสามารถเรียกใช้แอปพลิเคชันพร้อมกัน (แนวทางของฉันจะต้องเปลี่ยนแปลงเล็กน้อยเพื่อสร้างไฟล์ในโครงสร้างผู้ใช้ แต่นั่นเป็นเรื่องเล็กน้อย)
- ล้มเหลวหากกฎของไฟร์วอลล์เข้มงวดเกินไป
- ทำให้ผู้ใช้ที่น่าสงสัย (ซึ่งฉันได้พบในป่า) สงสัยว่าคุณกำลังทำอะไรอยู่เมื่อโปรแกรมแก้ไขข้อความของคุณอ้างสิทธิ์ในซ็อกเก็ตเซิร์ฟเวอร์
ฉันมีความคิดที่ดีสำหรับวิธีแก้ปัญหาการสื่อสาร Java แบบอินสแตนซ์ใหม่ไปยังอินสแตนซ์ที่มีอยู่ในแบบที่ควรใช้กับทุกระบบ ดังนั้นฉันจึงจบคลาสนี้ในเวลาประมาณสองชั่วโมง ใช้งานได้เหมือนมีเสน่ห์: D
มันขึ้นอยู่กับวิธีการล็อกไฟล์ของRobert (เช่นในหน้านี้) ซึ่งฉันก็ใช้มาตลอด เพื่อบอกอินสแตนซ์ที่ทำงานอยู่แล้วว่าอินสแตนซ์อื่นพยายามเริ่มต้น (แต่ไม่ได้) ... ไฟล์จะถูกสร้างและลบทันทีและอินสแตนซ์แรกใช้ WatchService เพื่อตรวจจับการเปลี่ยนแปลงเนื้อหาของโฟลเดอร์นี้ ฉันไม่อยากจะเชื่อเลยว่านี่เป็นแนวคิดใหม่เนื่องจากพื้นฐานของปัญหาเป็นอย่างไร
สิ่งนี้สามารถเปลี่ยนได้อย่างง่ายดายเพียงแค่สร้างและไม่ลบไฟล์จากนั้นสามารถใส่ข้อมูลที่อินสแตนซ์ที่เหมาะสมสามารถประเมินได้เช่นอาร์กิวเมนต์บรรทัดคำสั่งและจากนั้นอินสแตนซ์ที่เหมาะสมก็สามารถทำการลบได้ โดยส่วนตัวแล้วฉันจำเป็นต้องรู้ว่าเมื่อใดที่จะกู้คืนหน้าต่างแอปพลิเคชันของฉันและส่งไปที่ด้านหน้า
ตัวอย่างการใช้งาน:
public static void main(final String[] args) {
if (!SingleInstanceChecker.INSTANCE.isOnlyInstance(Main::otherInstanceTriedToLaunch, false)) {
System.exit(0);
}
System.out.println("Application starts properly because it's the only instance.");
}
private static void otherInstanceTriedToLaunch() {
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.*;
public enum SingleInstanceChecker {
INSTANCE;
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;
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) {
installOtherInstanceLaunchAttemptWatcher(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread);
} else {
createAndDeleteOtherInstanceWatcherTriggerFile();
}
}
optionallyInstallShutdownHookThatCleansEverythingUp();
return ret;
}
private void createAndDeleteOtherInstanceWatcherTriggerFile() {
try {
final RandomAccessFile randomAccessFileForDetection = new RandomAccessFile(DETECTFILE, "rw");
randomAccessFileForDetection.close();
Files.deleteIfExists(DETECTFILE.toPath());
} 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) {
try {
watchService = FileSystems.getDefault().newWatchService();
} catch (IOException e) {
e.printStackTrace();
return;
}
final File appFolder = new File("").getAbsoluteFile();
final Path appFolderWatchable = appFolder.toPath();
try {
appFolderWatchable.register(watchService, StandardWatchEventKinds.ENTRY_DELETE);
} catch (IOException e) {
e.printStackTrace();
return;
}
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) {
try {
Thread.sleep(POLLINTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
final WatchKey wk;
try {
wk = watchService.poll();
} catch (ClosedWatchServiceException e) {
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();
}
}
}