『Effective Java 第3版』の正誤表 [Effective Java 第3版]
『Effective Java 第3版』の正誤表を私のホームページに用意しました。
「正誤表」のタグを選んでもらえれば表示されます。
2018-10-31 17:10
コメント(0)
メルペイで5か月が過ぎました [プログラマー現役続行]
2018年6月1日にメルペイ社に入社(メルカリから出向の形態)して働き始めて5か月が過ぎました。
【仕事】メルペイが提供するサービスを構成するバックエンドのマイクロサービスの1つの開発を担当しています。開発言語はGo言語です。マイクロサービスが提供するAPIはgRPCベースですので、提供する機能の仕様をprotoファイルに書く作業をしばらく行っていました(「API仕様を書く」)。
最初から最終版のAPI仕様を書くことは不可能ですが、最初の仕様ができあがったので、テスト方法の検討と実装です。私が担当しているマイクロサービスは複数のマイクロサービスに依存しているので、どのようにe2eテストコードを書くかを検討しました。要件としては、以下の項目を最初に考えました。
【学習】長年、デジタル複合機のコントローラソフトウェア開発に従事していて、組み込み系がほとんどだったので、使っているクラウド技術は全く初めてのものばかりです。基盤としてはKubernetesを使っているので、読むべき本をAmazonで自分なりに調べて、『Kubernetes in Action』を選びました。読みながら実際に手を動かして学習してきました(まだ全部は読み終えていません)。本に従って、開発環境を整備したり、Google CloudにKubenetesを設定しながら学習することできます。
【勤務時間】基本的に7時30分頃に出社して、16時過ぎには退社します。通勤時間は、往復で4時間を超えています。16時過ぎには退社するので、19時過ぎに行われることが多い各種イベント(社内向け、社外向け)に出席することはありません。
【副業】副業をそのものは禁止されていませんが、メルペイ社に入社前から行っていた以下の副業は続けています。
【仕事】メルペイが提供するサービスを構成するバックエンドのマイクロサービスの1つの開発を担当しています。開発言語はGo言語です。マイクロサービスが提供するAPIはgRPCベースですので、提供する機能の仕様をprotoファイルに書く作業をしばらく行っていました(「API仕様を書く」)。
最初から最終版のAPI仕様を書くことは不可能ですが、最初の仕様ができあがったので、テスト方法の検討と実装です。私が担当しているマイクロサービスは複数のマイクロサービスに依存しているので、どのようにe2eテストコードを書くかを検討しました。要件としては、以下の項目を最初に考えました。
- 担当するマイクロサービスを起動して、gRPCを本当に呼び出してテストできること
- 依存するマイクロサービスのgRPCを本当に呼び出すこと
- 依存するマイクロサービスの振る舞いを、テストコードに記述できること(RPCが呼び出されたら、返すレスポンスをテストコードで記述できること)
- テストを実行したら、担当するマイクロサービスのカバレッジが取れること
- このe2eテストをCIで実行できること
【学習】長年、デジタル複合機のコントローラソフトウェア開発に従事していて、組み込み系がほとんどだったので、使っているクラウド技術は全く初めてのものばかりです。基盤としてはKubernetesを使っているので、読むべき本をAmazonで自分なりに調べて、『Kubernetes in Action』を選びました。読みながら実際に手を動かして学習してきました(まだ全部は読み終えていません)。本に従って、開発環境を整備したり、Google CloudにKubenetesを設定しながら学習することできます。
【勤務時間】基本的に7時30分頃に出社して、16時過ぎには退社します。通勤時間は、往復で4時間を超えています。16時過ぎには退社するので、19時過ぎに行われることが多い各種イベント(社内向け、社外向け)に出席することはありません。
【副業】副業をそのものは禁止されていませんが、メルペイ社に入社前から行っていた以下の副業は続けています。
- 技術書の翻訳
- 講演や技術教育
2018-10-31 07:03
コメント(0)
ロック(ミューテックス)の再入可能性 [プログラマー現役続行]
Go言語で提供されている
私自身がマルチスレッドプログラミングを本格的に行ったのは、Solaris 2.3を用いていたFuji Xerox DocuStation IM 200の開発です。当時、Solaris 2.3で提供されていたC言語用のスレッドライブラリ(POSIX Threadが登場する前です)は、再入可能なロックは提供していませんでした。そのためデッドロックが発生すると、場合によってはC++のメンバー関数の分割を行ったものです。privateのロックを獲得しないメンバー関数と、ロックを獲得してprivateのメンバー関数を呼ぶpublicの公開用のメンバー関数へと分割します。この分割については、上記の『プログラミング言語Go』にも書かれています。
IM 200が商品化された後の1996年にJavaを学び始めた時に、ロックが再入可能だというのは非常に魅力的に思えました。Javaの影響を受けて、2000年に設計したC++用のスレッドライブラリ(「clibと呼ばれるライブラリー開発の思い出」)では、再入可能なロックを提供する
2003年から2009年に従事したコントローラソフトウェア開発では、clibを用いてマルチスレッドプログラミングをかなり行ったのですが、再入可能であるために起きる問題に悩まされて、マルチスレッドプログラミングの難しさを痛感したものです。幸い、その開発は完全なテスト駆動開発でしたので、「マルチスレッドプログラミングにおける重要な4要件」を満たしており、様々な問題を解決する経験を積むことができました。このソフトウェア開発で経験した問題は、『Effective Java 第3版』でJoshua Blochが述べていることそのものでした。
活性エラーはデッドロックとして表面化しますが、安全性エラーはコードが動作しているように見えるが正しく動作していないという問題を引き起こします。特に競合状態(race condition)により引き起こされる問題は、現象の再現性も低く、調査が非常に難しいことが多いです。
2013年7月から2015年5月まで行ったコントローラソフトウェア開発は、私にとっては4回目のコントローラソフトウェア開発でしたが、マルチスレッドプログラミングの複雑さをGo言語のゴルーチンとチャネルで軽減できないかの検証も狙っていました。しかし、やはりデジタル複合機のコントローラソフトウェアは複雑であり、デッドロックはよく発生しましたし、競合状態も発生しました。幸い、Go言語には競合検出機能が提供されているので、その点は助かりました。
現在、従事しているソフトウェア開発では、再入可能性が問題となるような複雑なソフトウェア開発は行っていません。その点では、4回のデジタル複合機のコントローラソフトウェア開発は、私に貴重な経験を与えてくれたと言えます。
sync.Mutex
は、再入可能(re-entrant)ではありません。それについては、『プログラミング言語Go』(p,306)には次のように書かれています。Go のミューテックスが再入可能ではないことには正当な理由があります。ミューテックスの目的は、共有された変数のある種の不変式がプログラム実行中の重要な時点で維持されているのを保証することです。不変式の一つは、「共有された変数へアクセスするゴルーチンが存在しない」*2ですが、ミューテックスが保護しているデータ構造に特有の追加の不変式が存在するかもしれません。一つのゴルーチンがミューテックスロックを獲得したときには、そのゴルーチンは不変式が維持されていると想定するでしょう。ロックを保持している間に共有された変数が更新され、一時的に不変式が破られるかもしれません。しかし、ロックを解放するときには、秩序が回復されて不変式が再び維持されていることを保証しなければなりません。再入可能なミューテックスは他のゴルーチンが共有された変数へアクセスしていないことを保証するでしょうが、それらの変数の追加の不変式を保護することはできません。一方、Javaのオブジェクトのロック(モニター)は、再入可能です。つまり、同じスレッドが同じロックを複数回獲得できます。
*2 訳注:ゴルーチンがミューテックスロックを獲得する前の時点とそのロックを解放した時点で「共有された変数へアクセスするゴルーチンが存在しない」という意味です。この段落は、ミューテックスロックが獲得されていない場合の不変式について言及しています。
私自身がマルチスレッドプログラミングを本格的に行ったのは、Solaris 2.3を用いていたFuji Xerox DocuStation IM 200の開発です。当時、Solaris 2.3で提供されていたC言語用のスレッドライブラリ(POSIX Threadが登場する前です)は、再入可能なロックは提供していませんでした。そのためデッドロックが発生すると、場合によってはC++のメンバー関数の分割を行ったものです。privateのロックを獲得しないメンバー関数と、ロックを獲得してprivateのメンバー関数を呼ぶpublicの公開用のメンバー関数へと分割します。この分割については、上記の『プログラミング言語Go』にも書かれています。
IM 200が商品化された後の1996年にJavaを学び始めた時に、ロックが再入可能だというのは非常に魅力的に思えました。Javaの影響を受けて、2000年に設計したC++用のスレッドライブラリ(「clibと呼ばれるライブラリー開発の思い出」)では、再入可能なロックを提供する
CSynchronizer
というクラスを用意しました。そのライブラリを用いた最初の商品開発では、オブジェクトを介してスレッド間で同期するという設計は行われていませんでしたので、再入可能であることはそれほど利点がありませんでした。2003年から2009年に従事したコントローラソフトウェア開発では、clibを用いてマルチスレッドプログラミングをかなり行ったのですが、再入可能であるために起きる問題に悩まされて、マルチスレッドプログラミングの難しさを痛感したものです。幸い、その開発は完全なテスト駆動開発でしたので、「マルチスレッドプログラミングにおける重要な4要件」を満たしており、様々な問題を解決する経験を積むことができました。このソフトウェア開発で経験した問題は、『Effective Java 第3版』でJoshua Blochが述べていることそのものでした。
再入可能ロックは、マルチスレッドのオブジェクト指向プログラムの作成を単純化しますが、活性エラーを安全性エラーに変える可能性があります。
『Effective Java 第3版』(p.319)
活性エラーはデッドロックとして表面化しますが、安全性エラーはコードが動作しているように見えるが正しく動作していないという問題を引き起こします。特に競合状態(race condition)により引き起こされる問題は、現象の再現性も低く、調査が非常に難しいことが多いです。
2013年7月から2015年5月まで行ったコントローラソフトウェア開発は、私にとっては4回目のコントローラソフトウェア開発でしたが、マルチスレッドプログラミングの複雑さをGo言語のゴルーチンとチャネルで軽減できないかの検証も狙っていました。しかし、やはりデジタル複合機のコントローラソフトウェアは複雑であり、デッドロックはよく発生しましたし、競合状態も発生しました。幸い、Go言語には競合検出機能が提供されているので、その点は助かりました。
現在、従事しているソフトウェア開発では、再入可能性が問題となるような複雑なソフトウェア開発は行っていません。その点では、4回のデジタル複合機のコントローラソフトウェア開発は、私に貴重な経験を与えてくれたと言えます。
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- 作者: Alan A.A. Donovan
- 出版社/メーカー: 丸善出版
- 発売日: 2016/06/20
- メディア: 単行本(ソフトカバー)
2018-10-23 07:05
コメント(0)
『Effective Java 第3版』と『Java Puzzlers』ー パズル91 ー [Effective Java 第3版]
『Effective Java 第3版』の「項目88 防御的に
readObject
メソッドを書く」で言及されているのがパズル91です。パズル91 連続殺人犯(Serial Killer)
このプログラムは、オブジェクトを生成して、それがクラスの不変式に従っているかを検査しています。それから、プログラムはオブジェクトをシリアライズして、ディシリアライズし、そして、そのディシリアライズしたコピーもその不変式に従っているかを検査しています。 従っていますか? 従っていないとしたら、なぜですか?import java.util.*; import java.io.*; public class SerialKiller { public static void main(String[] args) { Sub sub = new Sub(666); sub.checkInvariant(); Sub copy = (Sub) deepCopy(sub); copy.checkInvariant(); } // シリアライズで引数をコピー(パズル80参照) static public Object deepCopy(Object obj) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(obj); ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray()); return new ObjectInputStream(bin).readObject(); } catch(Exception e) { throw new IllegalArgumentException(e); } } } class Super implements Serializable { final Setset = new HashSet (); } final class Sub extends Super { private int id; public Sub(int id) { this.id = id; set.add(this); // 不変式の確立 } public void checkInvariant() { if (!set.contains(this)) throw new AssertionError("invariant violated"); } public int hashCode() { return id; } public boolean equals(Object o) { return (o instanceof Sub) && (id == ((Sub)o).id); } }
2018-10-22 06:42
コメント(0)
『Effective Java 第3版』と『Java Puzzlers』ー パズル51 ー [Effective Java 第3版]
『Effective Java 第3版』の「項目83 遅延初期化を注意して使う」で言及されているのがパズル51です。
パズル51 何が言いたいの?(What's the Point?)
このプログラムは、2つの不変な値クラス(value class)、すなわち、そのイン>スタンスが値を表すクラスを持っています。1つのクラスは、整数座標を持つ面上の点を表しています。そして、2番目のクラスは、このパズルにちょっと色を添えています。main
プログラムは、2番目のクラスのインスタンスを生成して表示しています。このプログラムは、何を表示しますか?
class Point { protected final int x, y; private final String name; // 生成時にキャッシュされる Point(int x, int y) { this.x = x; this.y = y; name = makeName(); } protected String makeName() { return "[" + x + "," + y + "]"; } public final String toString() { return name; } } public class ColorPoint extends Point { private final String color; ColorPoint(int x, int y, String color) { super(x, y); this.color = color; } protected String makeName() { return super.makeName() + ":" + color; } public static void main(String[] args) { System.out.println(new ColorPoint(4, 2, "purple")); } }
2018-10-21 06:34
コメント(0)
『Effective Java 第3版』と『Java Puzzlers』ー パズル77ー [Effective Java 第3版]
『Effective Java 第3版』の「項目82 スレッド安全性を文書化する」で参照されているのは、パズル77です。
パズル77 ロックを混乱させる怪物(The Lock Mess Monster)
このプログラムは、ちょっとした職場シミュレーションを行います。退社時間まで仕事をする、あるいは、少なくとも仕事をしているふりをするワーカースレッドを、プログラムは開始します。 それから、決して退社時間にならないようにしようとしている邪悪な上司を表しているタイマータスクを、プログラムはスケジュールします。 最後に、良い上司を表しているメインスレッドは、ワーカーに退社時間だと告げて、ワーカーが終了するのを待ちます。 このプログラムは、何を表示しますか?import java.util.*; public class Worker extends Thread { private volatile boolean quittingTime = false; public void run() { while (!quittingTime) pretendToWork(); System.out.println("Beer is good"); } private void pretendToWork() { try { Thread.sleep(300); // 仕事中に寝ている? } catch (InterruptedException ex) { } } // 退社時間。作業者を待つ - 良い上司から呼ばれる synchronized void quit() throws InterruptedException { quittingTime = true; join(); } // 退社時間を取り消す - 邪悪な上司から呼ばれる synchronized void keepWorking() { quittingTime = false; } public static void main(String[] args) throws InterruptedException { final Worker worker = new Worker(); worker.start(); Timer t = new Timer(true); // デーモンスレッド t.schedule(new TimerTask() { public void run() { worker.keepWorking(); } }, 500); Thread.sleep(400); worker.quit(); } }
2018-10-20 07:11
コメント(0)
【社内連絡】『Effective Java 第3版』読書会参加者募集 [読書会]
(ブログに書いていますが、社内向けの内容です)
(Javaはメルカリグループでほとんど使われていないのと朝8時なので、参加者がいるかは疑問が残りますが)『Effective Java 第3版』の発売に伴い、読書会を開催したいと思います。『プログラミング言語Go』読書会と同じ理由でブログで募集します。
『Effective Java』の内容に関する質問に答えたり、解説したりするのは、過去は「Java研修」でしか行っていません。その主な理由は、内容を理解するために必要な基礎知識を研修を通して事前に学んでおいてもらうためです(「『Effective Java 第2版』は、やはり初心者向けではない」参照)。今回の読書会では、参加者の知識レベルに合わせて必要に応じて説明していきます(さすがにJavaを一から教えることはしません)。そのため、かなり長期間の読書会になる可能性があります。
希望者は、社内のslackで私(@yoshiki.shibata)へ知らせてください。
- 開催日: 毎週、木曜日 朝8時から9時まで(木曜日が休みの場合は休会)
- 開始日: 2018年11月1日(木)
- 場所: メルペイ社内会議室(六本木ヒルズ43F)
- 対象者: メルカリグループ社員だけでなく、メルカリグループ内で働いている技術者(業務委託、インターシップなど)。
- 進め方: 最初から読んでいきます。疑問点は、都度ディスカッションしていきます。
【追記(2018年10月31日)】参加申し込みがゼロでしたので、開催しないことになりました。
2018-10-18 06:26
コメント(0)
『Effective Java 第3版』と『Java Puzzlers』ー パズル 2 ー [Effective Java 第3版]
『Effective Java 第3版』で2番目に言及されているのはパズル2です。
私自身は、金銭計算に
英語版ならKindle版が購入可能です(紙の本も買えるようです)。Kindle版は見たことがないのですが、錯視図はどうなっているのでしょうか(誰か教えてください)。
パズル2 変革の時(Time for a Change)このパズルが言及されているのは『Effective Java 第3版』の「項目60 正確な答えが必要ならば、
次の文章問題を考えてみてください。トムは、$1.10するスパークプラグを購入するために自動車部品店に行きますが、財布の中にあるのは2ドル紙幣ばかりです。2ドル紙幣でスパークプラグの代金を支払ったとしたら、彼は、お釣りをいくら貰うでしょうか。この文章問題を解こうとしているのが、次のプログラムです。このプログラムは、何を表示しますか?public class Change { public static void main(String args[]) { System.out.println(2.00 - 1.10); } }
float
とdouble
を避ける」です。私自身は、金銭計算に
float
やdouble
を使うべきではないことをいつ、どこで学んだのか覚えていません。しかし、このパズルのようにfloat
やdouble
を使ったコードを見かけることがたまにあります。昨年は1回だけ見かけたので、その時は『Effective Java 第2版』の該当項目を教えてあげました。英語版ならKindle版が購入可能です(紙の本も買えるようです)。Kindle版は見たことがないのですが、錯視図はどうなっているのでしょうか(誰か教えてください)。
Puzzlers: Traps, Pitfalls, and Corner Cases" title="Java Puzzlers: Traps, Pitfalls, and Corner Cases">
Java Puzzlers: Traps, Pitfalls, and Corner Cases
- 出版社/メーカー: Addison-Wesley Professional
- 発売日: 2005/06/24
- メディア: Kindle版
2018-10-16 17:29
コメント(0)
『Effective Java 第3版』と『Java Puzzlers』ー パズル41ー [Effective Java 第3版]
『Effective Java 第3版』で最初に言及されているのは、パズル41の解答です。パズル41は、以下の通りです。
パズル41 フィールドとストリーム(Field and Stream)言及されているのは、このパズルの解答として書かれていた
次のメソッドは、1つのファイルを他のファイルへコピーし、I/Oエラーが発生しても、生成したすべてのストリームをクローズするように設計されていました。残念ながら、常にそのようには動作しません。なぜ動作しないのでしょうか? 修正することができますか?
static void copy(String src, String dest) throws IOException { InputStream in = null; OutputStream out = null; try { in = new FileInputStream(src); out = new FileOutputStream(dest); byte[] buf = new byte[1024]; int n; while ((n = in.read(buf)) > 0) out.write(buf, 0, n); } finally { if (in != null) in.close(); if (out != null) out.close(); } }
finally
節のコードです。この解答も間違っています。何が間違っているか分かりますか?『Effective Java 第3版』の「項目9 try-finally よりもtry-with-resources を選ぶ」に解説されています。} finally { if (in != null) { try { in.close(); } catch (IOException ex) { // closeが失敗しても、できることはない } } if (out != null) { try { out.close(); } catch (IOException ex) { // 再度、closeが失敗しても、できることはない } } }
2018-10-15 06:30
コメント(0)
【社内連絡】『プログラミング言語Go』読書会参加者募集 [読書会]
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- 作者: Alan A.A. Donovan
- 出版社/メーカー: 丸善出版
- 発売日: 2016/06/20
- メディア: 単行本(ソフトカバー)
(ブログに書いていますが、社内向けの内容です)
3年振りに読書会を開催したいと思います。本来なら、社内のslackで告知すればよいのですが、過去の読書会はブログで募集しているので、今回もブログに書きます(後で調べるときは、ブログの方が便利なので)。
希望者は、社内のslackで私(@yoshiki.shibata)へ知らせてください。
- 開催日: 毎週、月曜日 朝8時から9時まで(月曜日が休みの場合は休会)
- 開始日: 2018年10月29日(月)
- 場所: メルペイ社内会議室(六本木ヒルズ43F)
- 対象者: メルカリグループ社員だけでなく、メルカリグループ内で働いている技術者(業務委託、インターシップなど)。
- 進め方: 最初から読んでいきます。本来なら練習問題を解くことも重要なのですが、読書会の中では練習問題は解きませんので、各自で取り組んでみてください。
2018-10-14 07:29
コメント(0)
この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。