1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
/*
* $Id: PseudoFileSemaphore.java,v 0.0 2009/04/02 00:00:00 t-imamura Exp $
*
* Copyright (c) 2009 t-imamura, All rights reserved.
*/
package semaphore;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.regex.Pattern;
/**
* ファイルを利用した擬似セマフォ.
* <br>
* システムの一時ファイルディレクトリにロックファイルを作成し、
* バイト単位でロックをしてパーミット数を管理します。
*
* <pre>
* final PseudoFileSemaphore semaphore = new PseudoFileSemaphore(SEMAPHORE_NAME, PERMITS);
*
* // シャットダウンフック
* Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
* public void run() {
* semaphore.release();
* }
* }));
*
* // パーミットの取得を試みる
* if (semaphore.acquire()) {
* // 起動OK!
* JOptionPane.showMessageDialog(null, "アプリケーションを起動しました。",
* SEMAPHORE_NAME, JOptionPane.INFORMATION_MESSAGE);
* } else {
* // 起動NG!
* JOptionPane.showMessageDialog(null, "アプリケーションの起動上限に達しました。",
* SEMAPHORE_NAME, JOptionPane.ERROR_MESSAGE);
* }
* </pre>
*/
public class PseudoFileSemaphore {
/** ロックファイル拡張子 */
private static final String LOCK_FILE_EXT = ".lck";
/** 利用可能なパーミットの数 */
private final int permits;
/** ロックファイル */
private final File file;
/** ロックファイルチャネル */
private FileChannel channel;
/** ファイルロック */
private FileLock filelock;
/**
* PseudoFileSemaphoreを構築する。
* <br>
* <code>name</code> には、英数といくつかの記号(<code>-+=_(){}</code>)のみが利用できます。
*
* @param name アプリケーション(ロックファイル)名
* @param permits 利用可能なパーミットの数
*/
public PseudoFileSemaphore(String name, int permits) {
if (!Pattern.compile("^[-+=0-9A-Za-z_(){}]+$").matcher(name).matches()) {
// ファイル名として使える文字なら何でも良いが、無難な英数といくつかの記号のみ
throw new IllegalArgumentException("name cannot be used. name=" + name);
}
if(permits < 1){
throw new IllegalArgumentException("permits is small. permits=" + permits);
}
this.permits = permits;
this.file = new File(System.getProperty("java.io.tmpdir"), name + LOCK_FILE_EXT);
}
/**
* 一時ファイルディレクトリにロックファイルを作成する。
*
* @param file ロックファイルパス
* @param permits 最大取得可能上限
* @return ロックファイルチャネル
* @throws IOException 入出力エラーが発生した場合
*/
private FileChannel createLockFileChannel(File file, int permits) throws IOException {
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel channel = raf.getChannel();
raf.setLength(permits);
if (raf.length() < permits) {
raf.seek(raf.length());
for (long i = 0; i < permits - raf.length(); i++) {
raf.write(0xff);
}
} else if (raf.length() > permits) {
channel.truncate(permits);
}
return channel;
}
/**
* パーミットを解放し、セマフォーに戻します。
*/
public synchronized void release() {
if (this.filelock == null)
return;
try {
this.filelock.release();
this.filelock = null;
this.channel.close();
this.channel = null;
this.file.delete();
} catch (IOException e) {
// 例外は特に何もしない
}
}
/**
* 利用可能な場合に限り、このセマフォーからパーミットを取得します。
*
* @return パーミットが取得された場合は <code>true</code>
*/
public synchronized boolean acquire() {
try {
FileChannel channel = createLockFileChannel(this.file, this.permits);
FileLock lock = null;
for (int p = 0; p < this.permits; p++) {
try {
lock = channel.tryLock(p, 1, false);
if (lock != null && lock.isValid()) {
break;
} else {
lock = null;
}
} catch (OverlappingFileLockException e) {
// 既にロックされてたら次を試みる
continue;
}
}
if (lock != null) {
this.filelock = lock;
this.channel = channel;
} else if(channel != null) {
channel.close();
}
} catch (IOException e) {
// 例外が発生しても何もしない、パーミットが取れないだけ。
}
return (this.filelock != null);
}
} |
最終更新時間:2009年10月24日 16時18分49秒 指摘や意見などあればSandBoxのBBSへ。
PseudoFileSemaphore.java PseudoFileSemaphoreTest.java