Java : FileLock (ファイルロック) - API使用例
FileLock (Java SE 22 & JDK 22) の使い方まとめです。
ほとんどのメソッドにサンプルコードがあります。
API仕様書のおともにどうぞ。
概要
注意
- FileLock の動作はプラットフォームに依存します。詳細は API仕様をご確認ください。
- 本記事のコード例は Windows 10 で実行したものです。
FileLock クラスを使うと、複数のプロセスから使うファイルをロック制御できます。
ファイルをロックするには FileChannel.lock メソッドを使います。
次のコード例では、2つのプロセスから lock.txt ファイルをロックします。
片方のプロセスがロック中に、別のプロセスから FileChannel.lock でロックしようとすると、ロックが解除されるまで待機します。
public class Child {
public static void main(String[] args) throws IOException, InterruptedException {
final var pid = ProcessHandle.current().pid();
System.out.println(" child : start (pid=" + pid + ")");
TimeUnit.SECONDS.sleep(1);
final var file = Path.of("lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
System.out.println(" child : lock start (pid=" + pid + ")");
try (final var _ = fc.lock()) {
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(" child : *** lock OK! *** (pid=" + pid + ")");
System.out.println(" child : sleep 5 seconds ... (pid=" + pid + ")");
TimeUnit.SECONDS.sleep(5);
}
System.out.println(" child : lock end (pid=" + pid + ")");
}
System.out.println(" child : end (pid=" + pid + ")");
}
}
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("main : start");
// プロセスを2つ起動します。
final var builder = new ProcessBuilder("java", "Child").inheritIO();
final var p1 = builder.start();
final var p2 = builder.start();
p1.waitFor();
p2.waitFor();
System.out.println("main : end");
}
}
// 結果
// ↓
//> java Main
//main : start
// child : start (pid=2636)
// child : start (pid=10564)
// child : lock start (pid=2636)
// child : lock start (pid=10564)
// child : *** lock OK! *** (pid=2636)
// child : sleep 5 seconds ... (pid=2636)
// child : lock end (pid=2636)
// child : end (pid=2636)
// child : *** lock OK! *** (pid=10564)
// child : sleep 5 seconds ... (pid=10564)
// child : lock end (pid=10564)
// child : end (pid=10564)
//main : end
もしくは、FileChannel.tryLock メソッドも使えます。
lock メソッドとは違い、もしロックされているときは待機せずに null を返します。
public class Child {
public static void main(String[] args) throws IOException, InterruptedException {
final var pid = ProcessHandle.current().pid();
System.out.println(" child : start (pid=" + pid + ")");
TimeUnit.SECONDS.sleep(1);
final var file = Path.of("lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
System.out.println(" child : lock start (pid=" + pid + ")");
while (true) {
try (final var lock = fc.tryLock()) {
if (lock != null) {
System.out.println(" child : *** lock OK! *** (pid=" + pid + ")");
System.out.println(" child : sleep 5 seconds ... (pid=" + pid + ")");
TimeUnit.SECONDS.sleep(5);
break;
} else {
System.out.println(" child : *** lock NG! *** (pid=" + pid + ")");
System.out.println(" child : sleep 1 second ... (pid=" + pid + ")");
TimeUnit.SECONDS.sleep(1);
}
}
}
System.out.println(" child : lock end (pid=" + pid + ")");
}
System.out.println(" child : end (pid=" + pid + ")");
}
}
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("main : start");
// プロセスを2つ起動します。
final var builder = new ProcessBuilder("java", "Child").inheritIO();
final var p1 = builder.start();
final var p2 = builder.start();
p1.waitFor();
p2.waitFor();
System.out.println("main : end");
}
}
// 結果
// ↓
//> java Main
//main : start
// child : start (pid=4392)
// child : start (pid=748)
// child : lock start (pid=4392)
// child : *** lock OK! *** (pid=4392)
// child : sleep 5 seconds ... (pid=4392)
// child : lock start (pid=748)
// child : *** lock NG! *** (pid=748)
// child : sleep 1 second ... (pid=748)
// child : *** lock NG! *** (pid=748)
// child : sleep 1 second ... (pid=748)
// child : *** lock NG! *** (pid=748)
// child : sleep 1 second ... (pid=748)
// child : *** lock NG! *** (pid=748)
// child : sleep 1 second ... (pid=748)
// child : *** lock NG! *** (pid=748)
// child : sleep 1 second ... (pid=748)
// child : lock end (pid=4392)
// child : end (pid=4392)
// child : *** lock OK! *** (pid=748)
// child : sleep 5 seconds ... (pid=748)
// child : lock end (pid=748)
// child : end (pid=748)
//main : end
コンストラクタ
FileLock (AsynchronousFileChannel channel, long position, long size, boolean shared)
protected です。
独自にサブクラスを作ることは少ないと思いますので、コード例は割愛します。
FileLock (FileChannel channel, long position, long size, boolean shared)
protected です。
独自にサブクラスを作ることは少ないと思いますので、コード例は割愛します。
メソッド
Channel acquiredBy ()
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
try (final var lock = fc.lock()) {
final var ret = lock.acquiredBy();
System.out.println(fc == ret); // true
}
}
final FileChannel channel ()
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
try (final var lock = fc.lock()) {
final var ret = lock.channel();
System.out.println(fc == ret); // true
}
}
final void close ()
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
try (final var lock = fc.lock()) {
System.out.println(lock.isValid()); // true
}
}
// try-with-resources文を使わない例です。
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
final var lock = fc.lock();
try {
System.out.println(lock.isValid()); // true
} finally {
lock.close();
}
System.out.println(lock.isValid()); // false
}
abstract boolean isValid ()
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
try (final var lock = fc.lock()) {
System.out.println(lock.isValid()); // true
}
}
// try-with-resources文を使わない例です。
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
final var lock = fc.lock();
try {
System.out.println(lock.isValid()); // true
} finally {
lock.close();
}
System.out.println(lock.isValid()); // false
}
final boolean overlaps (long position, long size)
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
try (final var lock = fc.lock(100, 200, false)) {
System.out.println(lock.position()); // 100
System.out.println(lock.size()); // 200
System.out.println(lock.overlaps(0, 99)); // false
System.out.println(lock.overlaps(0, 100)); // false
System.out.println(lock.overlaps(0, 101)); // true
System.out.println(lock.overlaps(99, 1)); // false
System.out.println(lock.overlaps(100, 1)); // true
System.out.println(lock.overlaps(101, 1)); // true
System.out.println(lock.overlaps(299, 1)); // true
System.out.println(lock.overlaps(300, 1)); // false
System.out.println(lock.overlaps(301, 1)); // false
System.out.println(lock.overlaps(0, Long.MAX_VALUE)); // true
}
}
final long position ()
public class Child {
public static void main(String[] args) throws IOException, InterruptedException {
if (args.length != 3) {
throw new IllegalArgumentException();
}
final var type = args[0];
final var position = Long.parseLong(args[1]);
final var size = Integer.parseInt(args[2]);
println(type, "start");
TimeUnit.SECONDS.sleep(1);
final var file = Path.of("lock.txt");
try (final var fc = FileChannel.open(file,
StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.READ)) {
println(type, "tryLock position = %d size = %d".formatted(position, size));
try (final var lock = fc.tryLock(position, size, false)) {
if (lock != null) {
println(type, " *** lock OK! ***");
println(type, " position = " + lock.position());
println(type, " size = " + lock.size());
fc.position(position);
final var buff = ByteBuffer.allocate(size);
fc.read(buff);
println(type, " read = " + Arrays.toString(buff.array()));
TimeUnit.SECONDS.sleep(5);
} else {
println(type, " *** lock NG! ***");
}
}
}
println(type, "end");
}
private static void println(String type, String text) {
System.out.printf(" child %s : %s%n", type, text);
}
}
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
if (args.length != 4) {
throw new IllegalArgumentException();
}
System.out.println("main : start");
final var positionA = args[0];
final var sizeA = args[1];
final var positionB = args[2];
final var sizeB = args[3];
System.out.println("main : child A position = " + positionA + " size = " + sizeA);
System.out.println("main : child B position = " + positionB + " size = " + sizeB);
// 7バイトのデータを書き込みます。
final var file = Path.of("lock.txt");
final byte[] bytes = {10, 20, 30, 40, 50, 60, 70};
Files.write(file, bytes);
// プロセスを2つ起動します。
final var b1 = new ProcessBuilder("java", "Child", "A", positionA, sizeA).inheritIO();
final var b2 = new ProcessBuilder("java", "Child", "B", positionB, sizeB).inheritIO();
final var p1 = b1.start();
TimeUnit.SECONDS.sleep(1);
final var p2 = b2.start();
p1.waitFor();
p2.waitFor();
System.out.println("main : end");
}
}
// 結果
// ↓
//> java Main 0 7 0 7
//main : start
//main : child A position = 0 size = 7
//main : child B position = 0 size = 7
// child A : start
// child B : start
// child A : tryLock position = 0 size = 7
// child A : *** lock OK! ***
// child A : position = 0
// child A : size = 7
// child A : read = [10, 20, 30, 40, 50, 60, 70]
// child B : tryLock position = 0 size = 7
// child B : *** lock NG! ***
// child B : end
// child A : end
//main : end
//
//> java Main 0 3 3 4
//main : start
//main : child A position = 0 size = 3
//main : child B position = 3 size = 4
// child A : start
// child B : start
// child A : tryLock position = 0 size = 3
// child A : *** lock OK! ***
// child A : position = 0
// child A : size = 3
// child A : read = [10, 20, 30]
// child B : tryLock position = 3 size = 4
// child B : *** lock OK! ***
// child B : position = 3
// child B : size = 4
// child B : read = [40, 50, 60, 70]
// child A : end
// child B : end
//main : end
abstract void release ()
このメソッドは close() と同等です。
release の代わりに、可能であれば try-with-resources文 を使うことをおすすめします。
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(
file, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
final var lock = fc.lock();
try {
System.out.println(lock.isValid()); // true
} finally {
lock.release();
}
System.out.println(lock.isValid()); // false
}
final long size ()
public class Child {
public static void main(String[] args) throws IOException, InterruptedException {
if (args.length != 3) {
throw new IllegalArgumentException();
}
final var type = args[0];
final var position = Long.parseLong(args[1]);
final var size = Integer.parseInt(args[2]);
println(type, "start");
TimeUnit.SECONDS.sleep(1);
final var file = Path.of("lock.txt");
try (final var fc = FileChannel.open(file,
StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.READ)) {
println(type, "tryLock position = %d size = %d".formatted(position, size));
try (final var lock = fc.tryLock(position, size, false)) {
if (lock != null) {
println(type, " *** lock OK! ***");
println(type, " position = " + lock.position());
println(type, " size = " + lock.size());
fc.position(position);
final var buff = ByteBuffer.allocate(size);
fc.read(buff);
println(type, " read = " + Arrays.toString(buff.array()));
TimeUnit.SECONDS.sleep(5);
} else {
println(type, " *** lock NG! ***");
}
}
}
println(type, "end");
}
private static void println(String type, String text) {
System.out.printf(" child %s : %s%n", type, text);
}
}
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
if (args.length != 4) {
throw new IllegalArgumentException();
}
System.out.println("main : start");
final var positionA = args[0];
final var sizeA = args[1];
final var positionB = args[2];
final var sizeB = args[3];
System.out.println("main : child A position = " + positionA + " size = " + sizeA);
System.out.println("main : child B position = " + positionB + " size = " + sizeB);
// 7バイトのデータを書き込みます。
final var file = Path.of("lock.txt");
final byte[] bytes = {10, 20, 30, 40, 50, 60, 70};
Files.write(file, bytes);
// プロセスを2つ起動します。
final var b1 = new ProcessBuilder("java", "Child", "A", positionA, sizeA).inheritIO();
final var b2 = new ProcessBuilder("java", "Child", "B", positionB, sizeB).inheritIO();
final var p1 = b1.start();
TimeUnit.SECONDS.sleep(1);
final var p2 = b2.start();
p1.waitFor();
p2.waitFor();
System.out.println("main : end");
}
}
// 結果
// ↓
//> java Main 0 7 0 7
//main : start
//main : child A position = 0 size = 7
//main : child B position = 0 size = 7
// child A : start
// child B : start
// child A : tryLock position = 0 size = 7
// child A : *** lock OK! ***
// child A : position = 0
// child A : size = 7
// child A : read = [10, 20, 30, 40, 50, 60, 70]
// child B : tryLock position = 0 size = 7
// child B : *** lock NG! ***
// child B : end
// child A : end
//main : end
//
//> java Main 0 3 3 4
//main : start
//main : child A position = 0 size = 3
//main : child B position = 3 size = 4
// child A : start
// child B : start
// child A : tryLock position = 0 size = 3
// child A : *** lock OK! ***
// child A : position = 0
// child A : size = 3
// child A : read = [10, 20, 30]
// child B : tryLock position = 3 size = 4
// child B : *** lock OK! ***
// child B : position = 3
// child B : size = 4
// child B : read = [40, 50, 60, 70]
// child A : end
// child B : end
//main : end
final String toString ()
final var file = Path.of("R:", "java-work", "lock.txt");
try (final var fc = FileChannel.open(file, StandardOpenOption.CREATE,
StandardOpenOption.WRITE, StandardOpenOption.READ)) {
try (final var lock = fc.lock()) {
final var str = lock.toString();
// sun.nio.ch.FileLockImpl[0:9223372036854775807 exclusive valid]
System.out.println(str);
}
try (final var lock = fc.lock(100, 200, true)) {
final var str = lock.toString();
// sun.nio.ch.FileLockImpl[100:200 shared valid]
System.out.println(str);
}
}