ConcurrentHashMapを使うならatomicなメソッドを使おう
ConcurrentHashMapを使うときの注意点
タイトルの通りです。
ConcurrentHashMapは同期を取りつつ、パフォーマンスも優れたとても優秀なやつで、最近Webアプリケーションで、キャッシュみたいなものを作るときに使いました。
とはいえ同期を取るとはいっても以下のようなキーの存在を確認して、putするような複合アクションの呼び出し(いわゆるcheck-then-act)では同期されません。(別にこれは、従来のHahMapとかCollections.syncronizedMapとかでも同じです)
// atomicではない操作。 if(!map.containsKey(key)) { map.put(key, value); }
以下が実証コードで、実行すると高い確率で複数スレッドで同一キーへのputを行います。
(今回のケースだとあまり問題にならないですが、valueを更新するような操作だと更新内容が喪失する可能性はあります。
また同期化されない処理は後々厄介なバグを生んだりするそうです。参考資料より。)
Atomicな操作
で、こんな時のためにConcurrentHashMapには、キーがなければ値を追加するという上記のような操作を行うputIfAbsentメソッドがあります。
// 上とほぼ同等な内容だが、atomicな操作。
map.putIfAbsent(key, value)
atomicなメソッドはほかに、replace, removeなんかがあります。 というわけで、もし既存のソースでMapをConcurrentHashMapに置き換える時は、上記のようは複合アクションを行っていないかも確認した方が良いという話でした。
参考資料
Java並行処理プログラミング ―その「基盤」と「最新API」を究める―
- 作者: Brian Goetz,Joshua Bloch,Doug Lea
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/11/22
- メディア: 単行本
- 購入: 30人 クリック: 442回
- この商品を含むブログ (170件) を見る
play2.1で個人的なものを作った
play framework 2.1で個人的な課題を解消するためのサービスを作ってみた。
github https://github.com/kencharos/yakimashi/
herokuでのサンプル http://yakimashi.herokuapp.com/
動機
家族が増えて以来写真を撮ることが多くなり、写真は自分のMacbookのiPhotoで管理しているんだけど、
家族が写真を見たりするときに面倒。
またプリントしてアルバムに残したり、親戚に送ったりするので、写真の一覧を紙に印刷して、家族でこの写真は誰々に何枚みたいな作業を数か月に1回くらいやっている。
写真の一覧だと細部がわかりにくかったりするし、集計がすげえ面倒。
というわけでこれらを解消するためのものを勉強がてら作ろうというのが動機。
要望
- 家族各自のPC,iPadから見れる。
- 写真の一覧、詳細を確認できる。
- 写真ごとに送る人の設定とかができる。
- 基本的に家族でしか使わないからユーザー管理とかしない。
構成要素
勉強のため自分の仕事であんまり触ってないものを中心にやってみた。
メモ、忘備録
作っていてはまったことやこれはすげえと思ったことなど。
- 雑感として,playだとコード量がとても少ない(多分javascriptの方が多い。)。とはいえ学習コストは他のフレームワーク同様高いと感じた。
- 色々な方式をscalaの文法や関数でなるべく対処しようとしているんだと思うんだけど、おまじないとしか思えない記述が多すぎて色々戸惑う。(下のformの宣言とか、formの検証とか最初はよくわからなかった)
- playでformとcaseクラス、JSONとcaseクラスの変換を宣言的に記述できて面白い。
また、上記で特定の項目だけcaseクラスに設定したくないとか単純にマッピングできない場合にも対応できていて○
- play、サーバー起動中の修正が再起動なしで反映されて楽。
- ただし、コンパイルが微妙に遅く、viewとかrouteもコンパイルされるので、画面をちょっと直して5,6秒待つみたいなケースがある。
- view templateは@に続く箇所にscalaコードを記述できるので色々できるんだけど慣れるまで微妙。
- viewで使用するデータは、viewで宣言した引数のみで、JSPみたいな暗黙なリクエストとかセッションみたいなものは(宣言しない限り)出てこない。
viewの描画が関数の呼び出しになっているので、viewで色々できる危険性が減っている、、のかな。
- Actionを合成することで、Action前後のログ出力とか、ログイン判定とかができる。
- mongoDBが便利すぎて笑える。1対多のデータをそのままコレクションに突っ込めるとか。
- play-salatも超便利。case classのコンパニオンに特定のクラスを継承するだけで、CRUD諸々の機能が追加される。
- heroku でmongoHQを使う場合、herokuの環境変数(heroku configで確認)に、MONGOHQ_URLというmonngoDBのurlが設定されるのでこの値を設定する。設定するには、Procfileを作成し以下の形式でconf/application.confのプロパティを上書きする。
- -Dmongodb.default.uri="$MONGOHQ_URL"
playコマンドがヒープ不足で失敗する時
ちょっと興味があって[www.playframework.com/:title=Playframework]を触っています。
自宅のmacと会社のWindowsPCにそれぞれPlay2.1の環境を作って共有しているんだけど、
WindowsPCでPlayプロジェクトでplayコマンドをうつと、以下のようにコンソールにで起動に失敗する。
Error occurred during initialization of VM Could not reserve enough space for object heap Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit.
PCのスペックはWin7 32bit メモリ2G
ヒープ領域が確保できないようなので、playのスクリプトを見てみると、
が本体のようでこのファイルの中に、
java -Xms512M -Xmx1024M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M %DEBUG_PARAM% %JAVA_OPTS% -Dfile.encoding=UTF-8 -Dplay.version="%PLAY_VERSION%" -Dsbt.ivy.home="%~dp0..\repository" -Dplay.home="%~dp0." -Dsbt.boot.properties="%fp%sbt/sbt.boot.properties" -jar "%~dp0sbt\sbt-launch.jar" %*
とあるのでここを編集する。
Xms,Xmx,Xss,MaxPermSize全てのオプションを削除しても起動したのだけど、Xmxのでフォルトは64Mで心もとないので、Xmxを768Mに修正して無事起動した。
色々試した所、上記のPCのスペックだと、ヒープ領域とパーマネント領域の合計が1150Mを超えると失敗するようだった。
というかデフォルトでXmxが1Gは随分と富豪だなと感じる。
google driveのOCRを試すWebアプリを作ってみた
Ruby 認定資格Gold合格したので、何か作ろうかなと考えていた所、
Google DriveのOCRって使い物になるのかなと思い、
ファイルをアップロードしたらGoogle Driveに登録して、OCRのテキストを取得するような物を作ってみた。
Trying Google Drive OCR
利用するにあたって、GoogleのアカウントとDriveの利用、OAuth認証が必要です。
また、ここでアップロードされたファイルは、Driveに保存されます。
イメージはこちら
見て分かる通り、Driveに元画像とOCRの解析結果テキストが一つになった文書が作成されます。
テキストだけの取得は、色々調べたけどどうしてもページ内に埋め込めず断念。
というか、 OCRの精度は低い。あと日本語もまだ対応してないっぽい。
以下は作成時のメモ
Google Drive SDKのドキュメントはこちらにある。
Google Drive SDK — Google Developers
SDKとAPIがあって、SDKはChrome Web Storeに色々登録しろとか書いてあるけど、今回みたいなDriveへのファイルの登録とか取得だけだったらAPIだけでいい。SDKはDriveのUIに作成したアプリのメニューを追加したり連携したりする場合に使う。
APIを使う場合は、Google API ConsoleGoogle Accounts から、Drive APIを有効にし、Client IDとsecretを発行する。
今回作ったソースは https://github.com/kencharos/google-drive-oc-ruby に。
初めてGitをまともに使ったので、色々と変な事している気がする。
個人で実行する場合は上記のClient IDとsecretを、crient_secret.jsonに記載する。
アプリケーションは、heroku上で、sinatra,datamapper,haml などで作りました。
sinatraとてもいいです。
なお、herokuではファイルアップロードが一切できないと勘違い*1しており、最初はブラウザ側でFile APIを使用してbase64エンコードしてサーバーに送り、サーバー側でエンコードするような処理をしてた。
ファイル選択時にプレビュー出るのはその名残。
今回、初めてgit,github,ruby,herokuを使ってみました。
職場の、java, 占有ロックが基本の某バージョン管理ツール、起動するまで5分かかるサーバーという環境に比べると
色々と快適すぎて涙が出ます。
今後は、Rspec, Scala、html5の各API、ましなUI などもやってみる。
作成にあたり、以下のページを参考にさせていただきました。ありがとうございます。
Heroku上でSinatraアプリを動かすまでのまとめ - まちゅダイアリー(2011-10-02)
HerokuアプリをGitHubにもプッシュする - アインシュタインの電話番号
HerokuとGitHubの両方にプッシュする時の秘密にしたい値の扱い - アインシュタインの電話番号
*1:ファイルを書き込める領域が無いだけで、tmpfileは使える
Ruby始めました
仕事だとJavaしかやってないし、javaの仕事しかない業界だったりするので、
前から興味があったRubyの勉強始めました。
ついでなんで、Ruby技術者認定の取得も目指してます。
使ってるのはこの教科書
Ruby公式資格教科書 Ruby技術者認定試験 Silver/Gold対応 (EXPERT EXPASS)
- 作者: 増井雄一郎,小川伸一郎,(株)日立ソリューションズ藁谷修一,川尻剛,牧俊男,Rubyアソシエーション,CTCテクノロジー(株)監修
- 出版社/メーカー: 技術評論社
- 発売日: 2012/02/22
- メディア: 単行本(ソフトカバー)
- 購入: 5人 クリック: 204回
- この商品を含むブログ (10件) を見る
特異クラスおもしれーとかって思ってたらその辺はGold試験の範疇らしく、
Silver試験は組み込みクラスの挙動についてがメインのようで、暗記物が多くめんどい。
どのメソッドが破壊的メソッドなのかかどうかがわかりにくく*1、この辺を整理するのが吉かと思い、
とりあえず表にまとめてる。String,Array,Hashができれば多分対策としては十分と思われる。
*1:Scalaだと型でimutable,mutableを切り替えられるので、そこは静的型付けの利点なのかな
総当たりのパターンを作る
検証用のコードなんかではたまに、複数個のパラメータの設定の組み合わせを全部試すようなことをやることがあります。
例えばとあるメソッドに5個パラメータがあるとしてそれらのパラメータに値を設定する or 設定しない だけでも
2の5乗で32の組み合わせがあります。
全部の組み合わせをどうにか作成したいと思い、二進数の各桁の値で設定有無を判定するようなコードを書きました。
以下のような感じです。
public class Test { public static void main(String[] args) { final int SIZE = 5; for (int i = 0; i < Math.pow(2, SIZE); i++) { for (int j = 0; j< SIZE; j++) { int flg = (i >> j) % 2; System.out.print((flg == 0 ? "OFF" : "ON") + "\t"); } System.out.println(); } } }
値のon/offだけでなく、色々なバリエーションがある場合はこれでは難しいので、
ちょっと再帰を使ってみて、scalaで書いてみる。
要するに直積を求めるものですね。
def product[A](list:List[A]*): List[List[A]] = list match { case Seq() => List(Nil) case _ => list.head.flatMap(x => product(list.tail: _*).map(y => x :: y)) } product(List(1,2,3), List("A","B","C"), List(7,8,9)) foreach println
実はこんなことしなくても、scalaでは
for(a<-List(1,2,3);b<-List("A","B","C");c<-List(7,8,9)) yield { a::b::c::Nil }
で同じ事ができるのですが、引数を可変にしたいということでこうなりました。
なお、今回のコードは末尾再帰ではないので引数が増えると簡単にOutOfMemoryErrorが起きます。
頑張って末尾再帰にしたり、Stream使ってみたりしましたが、
何だか旨くいかなかったので、何か方法を考える。。。
FPは奥が深い。