- PR -

ディレクトリの排他制御

投稿者投稿内容
どんたくお
ベテラン
会議室デビュー日: 2005/08/29
投稿数: 88
投稿日時: 2007-02-27 12:17
みなさん、こんにちは。
どんたくおです。

動作環境ですが
JDK 1.5を使用しております。
Windows/Linuxを想定しております。


現在、ファイルを管理するアプリケーションを作成しております。
そこで、以下のようなことがしたいと思っています。

1.work内ディレクトリから、mainディレクトリへファイルのコピー
 これだけであれば、Jakarta commons様のお力を借りれば、住むこ
2.work内ディレクトリの排他制御を行う。
 これが、どういう風な実装があるか、現在悩んでおります。



現在、自分が考えているのは、
[work内ディレクトリを操作するプログラム]
1.work内に「opr.txt」があるか確認
  >ある場合は、スリープ処理を行う。
2.「opr.txt」がなければ、作成
3.work内にファイルをコピー
4.↑が完了後、opr.txtを削除
というような流れで考えています。
work内ディレクトリの排他制御は、プログラムがwork内にファイル
を格納する前に、作業していることを現す空ファイルを作成し、
検地できればよいと思っておりますが、もう少しよりやり方がない
か探しているところであります。

このような、プログラムでディレクトリに排他制御のようなものを
かける方法につきまして、妙案がありましたら、ぜひご教授いただけ
るとありがたいです。


以上です。
よろしくお願いします。
やじゅう
常連さん
会議室デビュー日: 2005/08/10
投稿数: 34
お住まい・勤務地: 野獣の住処
投稿日時: 2007-02-27 15:24
> 1.work内に「opr.txt」があるか確認
>   >ある場合は、スリープ処理を行う。
> 2.「opr.txt」がなければ、作成

ファイルの有無だけで判定すると、ファイルを作成したプロセスがファイルを削除する前にクラッシュした場合、永久に処理できなくなってしまいます。
ロックファイルを用いる(上記の例ならば、"opr.txt"に排他ロックを掛けられるか否かで判断する)のが、常套手段ではないでしょうか。
coasm
大ベテラン
会議室デビュー日: 2001/11/26
投稿数: 237
投稿日時: 2007-02-27 22:37
引用:

[work内ディレクトリを操作するプログラム]
1.work内に「opr.txt」があるか確認
  >ある場合は、スリープ処理を行う。
2.「opr.txt」がなければ、作成
3.work内にファイルをコピー
4.↑が完了後、opr.txtを削除
というような流れで考えています。


これでは、完全な「排他制御」は実現できません。
1. プロセスA が opr.txtがあるか確認。ないと認識する。
2. プロセスB が opr.txtがあるか確認。ないと認識する。
3. プロセスA が opr.txtを作成する。新規作成。
4. プロセスB が opr.txtを作成する。既存ファイルを上書き。
という動きになることを回避できないからです。

ロック可能性の判定とロックの獲得は、アトミックな操作で一度に行う必要があります。

APIレベルならopen(file, O_CREAT|O_EXECL)で実現可能ですが、javaではできません。
一般的には、mkdir() か renameTo() でロックファイル操作を行うことで代用します。

素直に java.nio.channels.FileLock を使うのが簡単かつ確実でしょう。

どんたくお
ベテラン
会議室デビュー日: 2005/08/29
投稿数: 88
投稿日時: 2007-03-08 23:59
やじゅうさま、coasmさま、ご返信ありがとうございます。
また、ご返信が遅くなりまして、すみませんm(_|_)m


やはり、自分の浅はかな考えのまま実装しなくて良かったです。
確かに、不完全な排他になってしまい、特にロックをかけてサーバが
停止した場合、ロックし続けてしまう問題につきましては、大変な
ことになるところでした。

皆様のご意見を元にしまして、java.nio.FileLockを使って、ロック
をかけたのですが、少し不可思議な現象に悩まされております。

以下のコードです。
コード:
	File file = "./hoge.lock";
	RandomAccessFile ra = new RandomAaccessFile(file, "rw");
	// 排他ロックをかけます。
	FileChannel chanel = ra.getChannel();
	FileLock lock = chanel.tryLock();
	if (lock == null) {
		throw new Exception("ロックです。");
	}
	

	Thread.sleep(5000);
	
	// 後の処理
	if (chanel.isOpen()) {
		if (lock != null) 
			lock.release();
	}
	


Sleepは、同時実行を発生させるために、わざとかけています。

というコードを、サーブレットで動かして、同じタイミングで二つの
ブラウザから上記を実行するサーブレットのURLを叩きました。
すると、手前のWindowsXP PROでは、同時にアクセスがあった場合、
例外が返されるのですが、RedHatLinux ES4で実行すると、二つのブ
ラウザで、ロックが取得されており、例外が返されず、排他制御が
できませんでした。

tryLock(0, Long.MAX_VALUE, true)などのようにしても、結果は同じ
でした。

JAVAのAPIを読むと、OSの実装しだいというような項目もあり、RedH
atではうまくいかないのかな・・・と思っております。

もし、FileLockを用いたLinuxでの排他制御に関しまして、情報を
お持ちの方がおいでましたら、ご教授いただけると幸いです。


よろしくお願い致します。
coasm
大ベテラン
会議室デビュー日: 2001/11/26
投稿数: 237
投稿日時: 2007-03-09 02:07
「サーブレットで動かして」ということは、同一JVM内のスレッドとして動作しているのですね?
ファイルロックの所有者はスレッドではなくてプロセスなので、同一プロセス(同一JVM)内の
排他制御に用いることはできません。
WindowsXPで排他できるという方が驚きです。

# WIN32の LockFile もプロセス単位のはずなんだが…

同一JVM内なら、適切なロックオブジェクトに対してsynchronized にするだけで排他できます。

[追記]
WIN32の LockFile のAPIドキュメントを見ると、
「ロック対象の領域の指定は、既にロックされている領域と重なってはなりません」
とありますね。
Windowsは自プロセスがロックしているものを再度ロックする操作を許さない。
linuxは再ロックを許容する。ということかな…



[ メッセージ編集済み 編集者: coasm 編集日時 2007-03-09 02:11 ]
どんたくお
ベテラン
会議室デビュー日: 2005/08/29
投稿数: 88
投稿日時: 2007-03-09 09:28
coasmさん、ご返信ありがとうございます。

> WindowsXPで排他できるという方が驚きです。
自分も、開発環境がWindowsなので、そちらではうまく動作しても、動作環境のLinuxで実行すると、うまく制御できずに、原因究明に時間がかかりました。

> 同一JVM内なら、適切なロックオブジェクトに対してsynchronized にするだけで排他できます。
ありがとうございます。
synchronizedで、一度ためしてみます。


結果につきましては、またご報告させていただきます。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-03-09 13:21
こんにちは。

個人的には…
リソースへの排他制御といえば「ミューテックス/セマフォ」だろと、思ってしまうのですが。

たしかJavaAPIにもセマフォが追加されたんじゃ…

java.util.concurrent.Semaphore
これって使えないんですかね?
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2007-03-09 13:27
VMより上位の層のロック取得ができないと、
クラスタ時で困りますね。

スキルアップ/キャリアアップ(JOB@IT)