戯言

つらつらと気づいたことを書いていきます。人狼とか。

スポンサーサイト


上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

java.util.Calendar#roll()


るる鯖人狼では、朝の4時に自動でバックアップを取得するために、専用のスレッドを生成しています。
プログラム起動時に、翌朝4時に実行するように設定し、その後は24時間ごとに実行しています。

翌朝4時を設定するために、

import java.util.Calendar;

Calendar cal = Calendar.getInstance();
cal.roll(Calendar.DATE, true);
cal.set(Calendar.HOUR_OF_DAY, 4);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);

とやっていました。

日付を1日進めて、翌朝の4時の実行を期待するプログラムだったのですが、3月末に起動した場合に3月1日の実行となってしまい、その後24時間毎に実行するために、延々と実行し続けるという恐ろしい状況になってしまいました…。

原因は、cal.roll(Calendar.DATE, true) で、rollメソッドは上下の位を変更することなく増減をさせます。
第二引数の true は +1 と同等なので、1を加えます。3月31日に実行すると3月1日になってしまいます。

cal.add(Calendar.DATE, +1) に変更して、4月1日になることを確認。きちんとメソッドの内容を調べてから使うべきというマヌケな話でした。

rollメソッドって、どういう時に使うんだろう…。


スポンサーサイト

java.util.ConcurrentModificationExceptionの原因と対策


java.util.Concurrent Modification Exceptionの原因と対策

ArrayListなどの非同期クラスのインスタンスが、別スレッドから変更されると、「勝手に変更されたよ?」ってことでjava.util.ConcurrentModificationExceptionが発生する。Webアプリケーションなどで、複数のスレッドから同時にListを触られるとよく出る例外ですね。

これの対策を調べていたら、Collections.synchronizedListという便利なものが標準で用意されていて、いちいちListを使っている箇所を全てsynchronized句で同期をとるなんて面倒なことをしなくても、対象インスタンスの生成時に、このインスタンスは共有するので同期化してくれるらしい。

こんな感じで書けば、対象のインスタンスを同期化してくれる。
private List<ChatMessage> messages
  = Collections.synchronizedList(new ArrayList<ChatMessage>());

これは便利だと思い、早速コードを変更して、しばらく様子を見ていたら、またあのjava.util.ConcurrentModificationExceptionが稀に発生している。1日に1回くらい。

また調べる。

どうやら、Collections.synchronizedListで、同期をしてくれるのは、message.add()メソッドやmessage.remove()メソッドなどを呼び出す時だけで、Iteratorを使ってListにアクセスする場合は, synchronized修飾子を使わなければならないようだ。

add/removeメソッドなどの呼び出し時に、内部的にsynchronizedブロックで同期化してくれるだけなので、当然と言えば当然なのですが。

というわけで、forループ内でListを参照しているところがあったので、ここを明示的にsynchronizedブロックで囲みました。また、これで様子を見よう・・・。

commons.io.FileUtilsの便利なメソッド


org.apache.commons.io.FileUtils


Javaでディレクトリ操作をする際に利用。
標準JDKだと、ファイルを含むディレクトリを削除できないため、再帰的に呼び出して1ファイルずつ削除する必要がありましたが、deleteDirectoryメソッドを使えば一発です。
import java.io.File;
import org.apache.commons.io.FileUtils;

File dir = new File("/tmp/removeDir");
FileUtils.deleteDirectory(dir);

ディレクトリコピーもcopyDirectoryで簡単。
import java.io.File;
import org.apache.commons.io.FileUtils;

File srcDir = new File("/tmp/srcDir");
File destDir = new File("/tmp/destDir");
FileUtils.copyDirectory(srcDir, destDir);
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。