2023/09/04

JVM Language Summit 2023 その1

このエントリーをはてなブックマークに追加

4年ぶりに開催されたJVM Language Summit (JVMLS)に参加してきました。

上の資料は8月31日に開催されたJJUGナイトセミナーの夏のLT大会でプレゼンした時の資料です。5分しかないLTなので、このエントリーで補足していきます。

JVM Language Summitとは

JVMLSは2008年から開催されたカンファレンスで、今年は4年ぶり13回目の開催になります。

2008年というとJVM上で動作する言語がいろいろでてきて、JVMでも動的型づけ言語をサポートするバイトコードを導入するかどうか議論をしていたころです。ちなみに、このバイトコードはinvokeDynamic (通称 indy)となり、Java 7から使えるようになりました。

そんな時に開催されたのが1回目のJVMLSです。

タイムテーブルを見ると、JRubyやScala、GroovyなどのJVM言語のセッションが並んでいることが分かります。

つまり、当時のJVMLSはJVM言語の人たちが一堂に会したカンファレンスでした。これがSummitになっている理由です。

しかし、だんだんとJVM言語のセッションは減っていき、その代わりにJVMとJavaの言語仕様などのセッションが増えてきました。今ではSummitは名前だけで、最新のJavaとJVMのカンファレンスになっています。

なお、今でもJVM言語関連でセッションを持つのはJRubyのCharles Nutterと、Graalの人たちぐらいになってしまいました。今年もCharlesはJRubyのセッションをするはずだったのですが、急用があったということでキャンセルになってしまいました。残念。

JVMLSの会期は3日間。その後にOpenJDKのコミッターによるアンカンファレンスのOpenJDK Committers' Workshopがあります。このワークショップは表向きはコミッターに限定しているようですが、実際にはコミッターでなくても参加できます。


櫻庭は2018年、2019年に引き続き、今回 3回目のJVMLSです。


JVMLSへの行き方

JVMLSの会場はOracleのサンタクララ キャンパスです。いわゆるシリコンバレーと呼ばれるところですね。

日本からだと、
  1. サンノゼ空港直行便
  2. 乗り継ぎでサンノゼ空港
  3. サンフランシスコ空港から陸路
のいずれかの方法で現地入りします。

日本からサンノゼへの直行便は今のところZIPAIRだけです。コロナ前まではANAにもあったのですが、今は運休中です。

乗り継ぎの場合、ロスアンジェルスかシアトルが一般的ですね。

サンノゼ空港からオラクルのキャンパスはそばなのですが、歩いたら1時間ぐらいかかります。バスや鉄道もあるにはあるのですが、本数がとても少ないです。

なので、ホテルまではUberかLyftを使うのがお勧めです。

今回、私は最後のサンフランシスコ空港を使う方法で現地入りしました。サンフランシスコ空港からサンタクララまではBARTとCaltrainという鉄道を乗りついで向かいます。ただ、Caltrainは本数が少ないので、駅で1時間ぐらい待つこともあります。

Caltrainの駅からは、UberかLyftを使うのがお勧めです。車社会なのですが、タクシーはほとんど走っていないので、UberもしくはLyftは必須のアプリです。

宿泊

Oracleのサンタクララキャンパスは市街地ではなく、まわりにはテック系企業の建物がポツン、ポツンとある程度です。

そのため、ホテルもまばらにある感じです。


青いスーツケースのピンがOracleの会場になります。

宿泊するホテルの候補になりそうなのが次の5ホテルぐらい。


一番近いのが、1番のHyatt House。Hyatt Houseは小規模のショッピングモールであるRivermark Plazaにあります。上の地図だと一番右にあるホテルです。

Rivermark Plazaには、スーパーのSafewayやメキシカンのファストフードのChipotleなどもあり、利便性も高いです。

次に近いのがElementです。Elementはまだできたばかりの新しいホテルです。

Elementと一緒にStarbucksなどの複数の店舗も一緒にオープンしたのですが、イマイチ使い勝手はよくないです。スタバも妙に早く閉まるし...

どちらもビジネスホテル的なホテルですけど、日本の価格から考えるとかなり高いです。

Delta Hotelsはコロナ前まではBaltimore Hotelだったのですが、マリオットに買収されて名前が変わっていました。買収前はもっと安かったのですが、マリオット系列になって高くなってしまいました。

ここはハイウェイに面しているので、モーテル的な感じです。ホテルがポツンとあるだけで、Elementの方に行かないと店はない感じです。

Avatarはリーズナブルなホテルだったのですが、残念ながら休業中。ちなみに、Avatarから会場まで、徒歩で40分ぐらいかかります。

Avatarに隣接して、Avatarの朝食会場でもあるIHOPがあったのですが、廃業してました。IHOPのホットケーキ甘すぎなんですけど、たまに食べてみたくなるんですよね。

Avatarから会場に向かう途中にあるのが、Marriottです。敷地も広く、ホテル内にもいろいろ店があるのでいいのですが、なにぶんにも高い。会社が宿泊費を出してくれることでもないかぎり泊まれない感じですね。


今回、櫻庭はElementに宿泊しました。新しいのはいいのですが、やっぱりスーパーが近くにないのはちょっと痛い。

会場まで直線距離だと近いのですが、鉄道が通っているため迂回しなくてはならず、だいたい徒歩で20分ぐらいかかります。

気候

サンタクララは一年中暖かく、乾燥しているので、カラッとした気候です。日中は30℃を超えますが、夜は20℃ぐらいまで下がります。

JVMLSの会場も冷房がきついので、一枚羽織るものを持っていくといいと思います。

会場

会場のOracle AuditoriumはAgnews Historic Parkという公園の一角にあります。
_DSC0442

ここは元々病院だった建物を歴史的建造物として保存してある公園です。とはいうものの、病院のメインの建物から裏側はOracleのサンタクララキャンパスになっています(ちゃんと新しい建物もたってます)。
_DSC0959

このサンタクララキャンパスは以前はSun Microsystemsだったのですが、今はOracleです。Sunの時代の名残として、キャンパスをぐるりと一周する道はNetwork Circleで、Sun Fire Wayという道もあります。
_DSC0634 _DSC0462
会場のAuditoriumは古い講堂で、内部はドーム型になってます。なかなか趣のある建物です。
_DSC1170 _DSC2002

食事

JVMLSでは朝食とランチが出ます。また、2日目の夜にはレセプションがありますが、会場で軽食を食べるぐらいです。

なので、期間中は夕ご飯だけ自分で食べに行かないといけません。

ホテルがポツン、ポツンとあるのと同じようにレストランも散らばっています。Hyatt HouseのあるRivermark PlazaにはChipotoleなどのファストフードや、普通のレストランもあります。

これ以外だと、基本的にUber/Lyftで行く感じです。

今回何を食べたか列挙しておきます。

6日のBirks以外はすべてUber/Lyft利用です。

Birksはステーキ。ここは最近オープンした店のようです。
_DSC0556

7日のBanana LeafはJenkinsの作者であり、LaunchableのCEOである川口耕さんに連れて行っていただきました。
_DSC0971 _DSC1044

8日と9日はどちらもベトナム料理なのですが、8日にサンノゼのリトルサイゴンにあるPho Ha Noiにフォーを食べに行ったのですが、閉まっていたので近くにあるBanh Canh 3 Mienに行ったのでした。Bahn Cahnという麺ははじめてだったのですが、伊勢うどんみたいな感じ。

この旅行中、現金を使ったのはこのBanh Canh 3 Mienだけでした。リトルサイゴンは小さい家族経営の店が多いようで、現金だけのところが多いような感じです。

_DSC1569

ところが、後から分かったのですが、リトルサイゴンにはPho Ha Noiは2店舗あり、閉まっていたのは支店で本店の方は開いていたのでした。

で、9日にスタンフォード大学に遊びに行った後、パロアルトでご飯を食べようと街を歩いていたら、昨日行けなかったPho Ha Noiの支店が目の前に。

ということで、2日連続でベトナム料理となったわけです。

ちなみに、10日はOpenJDK Committers' Workshopに参加していた人たちはChipotleに行ったらしいです。



長くなってしまったので、JVMLS本編に関しては次のエントリーで。

2023/07/04

JJUG Java仕様勉強会 「Javaの並列/並行処理の基本」

このエントリーをはてなブックマークに追加

6/29にJJUGのJava資料勉強会で並列/並行処理についてプレゼンしてきました。資料はこちら。

 

Javaの並列/並行処理についてですが、Virtual Thread以前の仕様に関してまとめた感じです。

Javaの並列/並行処理のAPIはjava.util.concurrentパッケージ、いわゆるConcurrency Utilitiesにまとめられており、それほどいっぱいあるわけではないです。ただ、使う上でのノウハウ的なものがいっぱいあるので、言語仕様やAPIの仕様とは離れますが、そのノウハウ的なものもさわりだけ紹介しました。

 

Threadの基本

Javaで並列/並行処理を行う時に必ず使うことになるのが、Threadクラスです。

ThreadクラスはOSスレッドのラッパーであり、ThreadオブジェクトとOSスレッドは1対1に対応しています。ただし、Java 21で導入されるVirtual ThreadはJVMが管理する軽量スレッドなので、OSスレッドとは直接結びつきません。

このセッションでは、Virtual Threadについては触れないので、Threadと行った時はOSスレッドに結びついている従来のThreadをあつかいます。なお、この従来からあるThreadをVirtual Threadと区別するためにPlatform Threadと呼びます。

 

並列処理と並行処理という2つの言葉がありますが、Javaの世界からはこの2つは区別されません。使用できるCPUのコアが少なく、それに対しスレッドが多く存在する場合は並列(Concurrent)として実行されます。一方で、コアが十分にあるのであれば、並行(Parallel)として処理されます。

つまり、同じThreadクラスで書いておけば並列にも並行にも処理されることがあるということです。

そして、Threadオブジェクトに対して行う操作は、OSスレッドに対する操作になります。

 

Threadを使う上で意識しなければいけないのが、Threadのライフサイクルと、Threadの実行順序(スレッドスケジューリング)です。

Threadのライフサイクルとは、生成から廃棄まで。

new演算子でThreadオブジェクトを生成するとOSのスレッドも作成されます。このこともあって、Threadオブジェクトの生成には時間もかかりますし、メモリ使用量も大きくなります。

一方のスレッドスケジューリングはどのような順番でスレッドを実行するかを決めます。使用できるコア数がスレッドよりも少ない場合、どこかでスレッドの切り替え(コンテキストスイッチ)が発生します。

コンテキストスイッチも時間もかかり、メモリも多く使用する処理です。このため、初期のJavaではなるべくコンテキススイッチが発生しないようにスケジューリングされていました。

しかし、プログラム内でThread.yeildやObject.waitなどが使用されると、コンテキストスイッチが発生します(本当にコンテキストスイッチを行うかどうかはスケジューラーが決めます)。頻繁なコンテキストスイッチはパフォーマンス劣化の原因になります。

このようなことから、スレッドの生成/廃棄の管理や、スレッドスケジューリングはなるべくJVM側で管理させ、私たち開発者は非同期に処理されるタスクを記述することに注力すべきです。

そこで、登場したのがConcurrency Utilitiesというわけです。

 

Conccurency Utilities

Conccurency Utilitiesが導入されたのはJava 5。2004年なので、もう20年近く前です。当時はマルチコアのCPUがようやく出てきたころで、ParallelよりはConcurrentの時代です。

Concurrency Utilitesが提供しているのは大別して次の4種類のAPIです。

  • 非同期タスクの実行・管理
  • 並列コレクション
  • アトミック操作
  • ロック

この時は非同期タスクの実行・管理に絞って説明しました。

非同期タスクの実行・管理に関する主なインタフェースとクラスは以下の4種類だけです。

  • ExecutorService: 非同期タスクの実行
  • Executors: ExectuorServiceのファクトリ
  • Runnable/Callable: 非同期タスク
  • Future: 非同期タスクの管理

Executorsだけがクラスで、他はインタフェースです。

他にもExecutorインタフェースやFutureTaskクラスなどがありますが、直接使うことは稀です。

ExecutorsクラスはExecutorServiceオブジェクトのファクトリメソッドを定義したクラスです。主に使われるファクトリメソッドは

  • newFixedThreadPool
  • newChacedThreadPool

の2メソッドです。いずれもスレッドを使いまわすスレッドプールを提供するExecutorServiceオブジェクトを生成します。

newFixedThreadPoolメソッドは固定のスレッド数だけでスレッドプールを作成します。スレッド数は引数で指定します。どちらかというと非同期タスクの応答性を重視したスレッドプールになります。

一方のnewCachedThreadPoolメソッドで生成するスレッドプールは、必要に応じてスレッドを生成するスレッドプールです。もし、使われていないスレッドがあれば使いまわします。こちらはスループット重視型のスレッドプールです。

他にもシングルスレッドで動作するnewSingleThreadExecutorメソッドや、タイマーなど周期的なタスク実行を行うためのnewScheduledThreadPoolメソッドがあります。

また、Java 8で、後述するWork-Stealingを使用したスレッドプールを提供するnewWorkStealingPoolメソッドが追加されました。次のJava 21ではVirtual Threadを使用するnewVirtualThreadPerTaskExecutorメソッドも提供される予定です。

 

ExecutorServiceインタフェースは非同期タスクの実行を行うためのインタフェースです。どのようにタスクの実行を行うかは実装クラスによって異なります。

基本的には使用するメソッドは2種類だけです。

  • Future<T> submit(Callable<T> task)
  • Future<T> submit(Runnable task, T result)
  • Future<T> submit(RUnnable task)
  • void close()

タスクの実行を行うのがsubmitメソッドです。タスクがCallableインタフェースかRunnableインタフェースの違いでオーバーロードが3種類あります。

closeメソッドはJava 19で追加されたメソッドです。なぜ今ごろになってcloseメソッドが追加されたかというと、try-with-resources構文が使えるようになったからです。

これまではshutdownメソッドもしくはshutdownNowメソッドを使う必要がありましたが、これからはtry-with-resourcesですね。

また、複数のタスクをまとめて登録できるinvokeAllメソッドとinvokeAnyメソッドもありますが、これらのメソッドは結果がでるまでブロックします。このため、ちょっと使いにくいメソッドでした。

Virtual Threadの導入に合わせてStructured Concurrencyが提案されており、invokeAll/invokeAnyメソッドはこちらに置き換えられていくと思います。ただ、Java 21ではまだPreview JEPなので、次の次のLTSに間に合うぐらいです。

 

さて、非同期タスクを記述するのがRunnableインタフェース/Callableインタフェースです。

両者の違いは戻り値があるかないかだけと思っている方が多いと思いますが、もう1点大きな違いがあります。

それは、RunnableインタフェースのrunメソッドはChecked Exceptionをスローできないのですが、Callableインタフェースのcallメソッドはできるという点です。

このため、Runnableインタフェースを使う場合、例外をスローさせるにはRuntime Exceptionでくるんでスローするしかありませんでした。しかも、Runtime Exceptionがスローされると、その例外はUncaught Exceptionとして扱われ、スレッドは何も言わずに死んでしまうのです。

UncaughtExceptionHandlerが登録してあれば検知できますが、それでも直接例外が発生したことは分かりません。

これに対し、CallableインタフェースであればChecked Exceptionもスローできます。また、例外が発生した場合、FutureインタフェースのgetメソッドなどでExecutionException例外としてcatchすることができます。

 

最後がFutureインタフェースです。

FutureインタフェースはExecutorServiceインタフェースのsubmitメソッドの戻り値になり、submitメソッドで登録した非同期タスクの管理を行います。

型パラメータはタスクの戻り値の型です。Runnableインタフェースを使用した場合はFuture<?>となります。

主に使うメソッドは

  • T get()
  • cancel()
  • Future.State state()

ぐらいでしょうか。

一番使うメソッドは、タスクの結果を取得するgetメソッドだと思います。ただ、getメソッドは結果が出るまでブロックすることに注意が必要です。

cancelメソッドはタスクのキャンセルのためのメソッドです。ただし、cancelメソッドでキャンセルできるかどうかは、タスクの書き方に依存します。

stateメソッドはJava 19で追加されたメソッドで、それまではisDone/isCancelledメソッドで状態を調べていました。stateメソッドを使うことで、もう少し詳しく状態を調べることができます。

Java 19では、resultNowメソッドとexceptionNowメソッドも追加されました。

この2つのメソッドはブロックをしないのですが、タスクが完了していないとIllegalStateException例外がスローされます。

 

後半は非同期タスクの書き方。

非同期タスクを書く上で安全性とスケーラビリティが重要になります。並列度が低い時代には安全性が重視されていましたけど、現在のように多くのコアが使えるようになるとスケーラビリティが重要になってきます。

ようするにスレッドセーフなクラスであったとしても、スケールできないものは使いものにならないわけです。とはいうものの、安全性がないがしろにされていいわけではありません。

安全性を確保しつつ、スケールするタスクを記述する必要があります。

まぁ、言うのは簡単ですけど、実際に書くのはむずかしいですね。

 

おまけでFork/Join FrameworkとCompletableFutureについても紹介しました。

特にFork/Join Frameworkは開発者が直接使うことはほぼないと思うので、使いかたよりも動作原理について紹介しました。

また、CompletableFutureは関数を連ねて処理を記述できる人であればとても使いやすいAPIなのですが、例外が扱いにくかったり、デバッグが難しいという点もあります。Virtual Threadであれば、例外やデバッグが容易になるので、書きやすい方で書けばよいと思います。

2023/06/19

合同勉強会 in 大都会 2023 Summer

このエントリーをはてなブックマークに追加

イベント続きの最後は、大都会での合同勉強会です。

久しぶりのIn Personの合同勉強会です。Javaな人は少数派なので、もうちょっと一般的な話ということでプレゼンの話をしてきました。

 

時間が20分と短いので、ほんとに導入編という感じです。

ちゃんとプレゼンテーションの目的意識を持ちましょうということをはじめに話しました。

誰(ターゲット)に対して話すのかということと、その人にどうしてもらいたいのか(アクション)を明確にしましょうということです。

ターゲットが変われば使う言葉も変わります。アクションもそうです。社内で予算確保のためのプレゼンであれば、ターゲットは予算を持っている上司、アクションは予算承認になります。予算承認のためのプレゼンで技術のおもしろさを説いてもしかたないですよね。その技術を使えば、どのくらい工数が減るとか、品質が上がるとか、なんらかの会社に対するメリットを説明するはずです。

ということで、ターゲットとアクションをちゃんと意識しましょうということです。

後半は資料の組み立てという体裁で話しましたが、どちらかというとプレゼンのストーリーの組み立てについてです。

ちゃんとストーリーを考えましょうということで、いきなりパワーポイントで資料を作り始めるのではなく、下書きをしましょうということです。

個人的には、プレゼンは準備8割、本番2割ぐらいに感じています。

もちろん準備8割の中に資料作成も含まれているのですが、あくまでも資料は話の補助的な役割であると思っています。

でも、資料先行しちゃう人が多いんですよね。

ということで、今回のまとめはこちら。

とりあえず、好評だったようでよかったです!

2023/06/18

JJUG CCC 2023 Spring

このエントリーをはてなブックマークに追加

2019年からずっと登壇してなかったのですが、久しぶりにJJUG CCCで登壇することになりました。

 

今回のお題はVirtual Threadsです。

話の流れなどは、今年の1月に行われたブリ会議で話したことをベースにしています。ただ、ブリ会議が20分のセッションだったのが、CCCでは45分なので、コンテンツ的にはかなり変更してあります。

 

前半はVirtual Threads導入の背景、後半がVirtual Threadsの使い方になっています。

なぜ今になってVirtual Threadsが導入されたかというのは、歴史的経緯があるので、そこをちゃんと抑えましょうということです。

というのも、Virtual Threadsの目的はスループット向上なのですが、現状の技術でスループットは向上できるからです。たとえば、CompletableFutureやリアクティブ系のライブラリやフレームワークを使えばスループットは向上できます。

これらの技術を使いこなせればいいのですが、なかなか難しい点もあります。たとえば、

  • 関数を連ねて処理を記述するので、従来とは考え方を変えなければならない
  • 例外処理が難しい
  • デバッグが難しい

最初の考え方は慣れてもらうしかないのですが、最後のデバッグはなかなか難しい問題です。というのも、処理が複数のスレッドに分割して実行されるので、スタックトレースが切れてしまうからです。

スタックトレースはスレッドごとに存在するので、スレッドをまたがって処理を行えば当然スタックトレースは分割されてしまいます。それがデバッグを難しくしてしまいます。

そういう問題を解決するのがVirtual Threadsというわけです。

 

とはいっても、Virtual Threadsは今までのスレッドと使い方が同じなので、特にこうしましょうというのはないです。

サーバー系のシステムでリクエストに応じてスレッドをよしなに作ってくれるようなフレームワークを使っているのであれば、なおさらVirtual Threadsを意識する必要はないです。

というわけで、そういうフレームワークがVirtual Threadsに対応してくれるのを待ちましょうというのが結論ですw

フレームワークがVirtual Threadsに対応したら、何もしなくてもスループットが向上するかもしれません。

というようなことをセッションで話したら、今週になってTomcatがVirtual Threadsに対応したというじゃないですか。

Tomcatでさえ対応したのだから、他のフレームワークもVirtual Threadに対応するのは意外に早いかもしれません。

 

ただし、Virtual Threadsに限らず並行処理で注意する点というのもあります。

並行処理で重要になるのは、安全に処理できるかということと、スケールするかどうかということです。しかも、この両者はなかなか両立しません。

同期化やロックなどを使用して安全な処理を記述すると、その部分はスケールせずに並列処理のボトルネックになってしまいがちです。

特にVirtual Threadsを使い始めると、スレッド数が今までとは桁違いに多くなるため、スケールできるかというのは重要になります。

では、どうすればいいか。

設計を見直して、synchronizedを使った同期化をなるべく減らしていくしかありません。そのためには、イミュータブルなデータをベースに考えるようにすればいいと思います。特にJava 16で導入されたRecord型が役に立つはずです。

どうしてもsynchronziedが外せない処理があるのであれば、そこはロックAPIのReentrantLockに書き換えましょう。というのも、synchronizedを使用するとVirtual Threadsが動作するOSのスレッドをブロックしてしまうためです。

ReentrantLockであれば、ブロックするのはVirtual Threadsだけになります。

というようなことを45分で喋ってきました。あまりにも、ギリギリまで喋ってしまったので、質疑応答の時間が取れなくなってしまいました。ごめんなさい。

 

当日は気持ちの余裕がなくて、あまり他のセッションは聞けなかったのですが、ちょっとした感想を書いておきます。

 

Apache Commons Math を使って、機械学習をやってみた

浅野さんの機械学習のセッション。といっても、扱っているの古典的なクラスタリングのk-means。

k-meansぐらいならライブラリ使わなくても、自力で書けるぐらいですね。というか、書いたことがありましたww

やっぱりPythonと比べると記述量が多くなってしまうのはしかたないですね。でも、OpenCVSとか使えば、もうちょっと減らせると思います。

 

浅野さんが列挙してくれた機械学習系のライブラリの1つにWekaがあるのですが、これは使ったことがありました。

でも、Wekaはソースがひどいんですよねぇ...

アルゴリズムをそのままコードに落とした感じで、読みやすさとかそういうのはまったく考慮してない感じ。まぁ、大学で作ってるし、しかたないんですけどね。

 

AI を利用した Java 開発の最新情報

てらださんのOpenAI系のお話なんだけど、なんだかなぁ...

てらださん、もうちょっとプレゼンのしかた考えようよ...

 

Java Bytecode Crash Course

もともとChristoph Engelbertさんのセッションだったのですが彼が体調不良ということで、急遽David Buckさんのセッションに差し替え。

バイトコードの読み方入門的な内容でした。内容的にはほとんど知っていることでしたが、やっぱりこういうのはおもしろいよね。人を選ぶとは思いますがw

 

今回のJJUG CCCは現地開催でストリーミングありでしたが、なかなか大変そうでした。コストもかかるしね。

今回ストリーミングで見ていただいた方も、次回はぜひ現地でお会いしましょう!

2023/06/17

JOnsen 2023

このエントリーをはてなブックマークに追加

コロナのおかげで開催できなかったJOnsenですが、4年ぶりに6月1日から2泊3日の日程で開催されることになりました。なお、今回からJJUGが主催のイベントになっています。

JOnsenは温泉に入りながらJavaについて語り合うアンカンファレンスで、今回で4回目の開催です。

第1回から参加しているさくらばは、当然今回も参加することにしました。

 

今回の開催場所はリゾナーレ八ヶ岳。参加人数は13人で、ちょっと少ないです。まぁ、コロナがあって、こういうイベントに参加するのを躊躇してしまう人も多いと思うので、しかたないですね。

 

初日

初日のスタートは14時から。他の参加者はみなお昼ぐらいに到着して、ランチを食べてから参加という感じですが、私だけちょっと早く現地入りしました。というのも、リゾナーレ内にあるマルサマルシェというフルーツパーラーでパフェ作り体験ができるからなのでした。

事前に予約しておいて、Bigパフェを作ってきました。

平日ということもあって、リゾナーレは未就学児を連れた家族連ればかり。パフェ体験も子供たちと一緒に作っている人たちばかりで、おじさんが1人で作っているなんて私だけですよw

下の写真のようにフルーツとパフェグラス渡されます。後は、冷蔵庫にあるホイップクリームとコンフィチュール、またコーンフレークやスプリンクルなどは使い放題です。

_DSC8361

事前にいろいろ考えてきたのですが、実際にパフェを作るのなんてはじめてなので、悪戦苦闘。ホイップクリームだけでなく、せめてジュレがほしかった...

ということで、パフェできました!

_DSC8402

 

さて、JOnsenの本編です。

会場はリゾナーレのイスキアという普段は宴会などに使われる場所です。

だいたいの日程は初日の午後と、2日目の午前、最終の午前にアンカンファレンス。2日目の午後はアクティビティを行うというものです。

ただ、2日目は台風の接近に伴う大雨の予定。雨でなければ乗馬などのアクティビティもあったのですが、急遽インドアのアクティビティになることになりました。

で、パフェ作りの人気が高いww さすがに2日連続でパフェというのも変なので、私はトンボ玉作成にしました。

_DSC8541 _DSC8546

 

アクティビティを決めた後に、まずは全員の自己紹介タイム。そして、アンカンファレンスで議論するトピック出しと続きます。最低でも1人1トピック。

トピックを付箋に書き出したら、各々トピックの説明を行います。もちろん、全部英語です。

_DSC8582 _DSC8584

付箋はホワイトボードに貼りだして、似ているトピックをグルーピング。その後、どのトピックを議論するかの投票です。

_DSC8639

実際に提案されたトピックがこちら。丸いシールが投票された票です。

_DSC8666

人気があったトピックからディスカッションしていきます。

今回は、人数も少ないので2グループに分けて、だいたい一方が技術的なトピックで、もう一方がコミュニティや働き方などの非技術的なトピックになっていました。

さくらばは結局、全部技術的なトピックの方に参加してました。

 

ディスカッションは写真のように丸テーブルで。そんなに固い感じではなく、緩い感じで。それにしても、久しぶりの英語だったので、ぜんぜん英語が出てこない。やっぱり日ごろから練習していないとダメですね。

_DSC8670 _DSC8686

 

そういえば、Steve Chinさんが車で参加予定だったのですが、いろいろと遅れて、1日目のディカッションの最後にやっとあらわれました。とりあえず、無事についてよかったです。

 

1日目のディナーはリゾナーレ内のイタリアン。なかなか本格的なイタリアンでした。

_DSC8714 _DSC8791 _DSC8819 _DSC8834

 

ディナー後にまたどこかで集まって飲んだりするのかなぁと思ったら、何もなく。みんな疲れたのか、そのまま就寝してしまいました。

でも、さくらばはJJUG CCC用の資料が終わってなかったので、泣く泣く資料作成。CCCの前のJOnsenはつらい...

 

2日目

朝からあいにくの雨。

朝ごはんはリゾナーレ内のYY Grillのビュッフェ。平日ということもあって、未就学児を連れたファミリーが多いです。

朝からしっかり食べましたよ。しかも、ここには自由に使えるソフトクリーマーがあるので、朝からソフトクリーム。

でも、ソフトクリームが固すぎて、うまく巻けませんでした...

_DSC8929 _DSC8935 _DSC8958

朝ごはんは同室のDavid Buckさんと行ったのですが、彼は実際にJavaを作っている人。しかも、HotSpotを作っているのです!

ちょうどJJUG CCCでVirtual Threadについてプレゼンするので、自分がVirtual Threadに関して考えていたことを聞いてもらい、間違いがないか確かめてもらいました。

この朝のセッションが、JOnsenで一番役に立ったかもしれませんw

 

さて、2日目の午前中はアクティビティの時間。

というわけで、トンボ玉を作る場所へ向かってみると... なんと今日はトンボ玉を作る先生がお休み!

他にも作る系のアクティビティは先生が休みのところ多くて、行き場を失った私たちは結局パフェの人たちを冷やかしにいくことに。

と思ったら、急にキャンセルが出たらしくテーブルが空いていたので、急遽クレープ作り体験ができることになりました!

とはいっても、クレープはすでに焼いてあって、それを飾り付けする感じです。

それにしても、普段はお客の大半がファミリーの店で、大人がほぼ占拠しているというのはなかなかレアな感じですね。ちなみに、私は店員さんにしっかり覚えられていましたw

_DSC8962 _DSC8970 _DSC8993 _DSC9003 _DSC9005 _DSC9012

それぞれ、パフェとクレープを作ってから、三々五々とランチへ。

私は、やはりリゾナーレ内にあるジビエの店で鹿肉バーガー。竹炭入りの黒いバンズというのはなかなかインパクトありますね。

_DSC9035 _DSC9045

 

さて、午後のアンカンファレンスも昨日と同じように2グループに分かれて行います。だいたい1つのトピックで40~50分ぐらい。

今日はProject CRaCや、関数型についてなどのディスカッションに参加。

Project CRaCは全然知らなかったのですが、起動時間を短縮させるためのプロジェクトなんですね。Graalのネイティブイメージとの比較などが議論の中心でした。

_DSC9117 _DSC9119 _DSC9129 _DSC9138

 

2日目のディナーはリゾナーレのすぐそばにあるジビエの八ヶ岳小僧という店。

店主が自ら狩りを行って、季節、季節のジビエを食べさせてくれるのだそうです。

で、私は鹿、猪、熊の盛り合わせと、鹿ユッケ。ご飯はたけのこご飯です。

鹿や猪は食べたことありますけど、熊ははじめて。脂がうまいww

_DSC9164 _DSC9188 _DSC9180 _DSC9208

 

最終日

最終日、朝ごはんを食べに行った時はまだパラパラと雨が降ってましたが、食べ終わったことろには晴れてきました。

そして、この日もソフトクリーム。昨日よりはうまく巻けたとは思いますが、やっぱり硬くてやりにくい...

_DSC9253

 

最終日のディスカッションは1トピックだけで、その後はラップアップ。

最後のディスカッションはVirtual Threadに関してですが、自分的には昨日の朝のDavidさんとの議論で話したいことを話してしまったのでしたw

Virtual Threadの技術的なこともそうですが、BrianのJava並行処理プログラミングの本が絶版で困るようねぇとか。本が売れないから再販もないだろうねぇとか。

_DSC9282 _DSC9278 _DSC9266 _DSC9275

そして、最後のラップアップで、ディスカッションのまとめを各々が発表する感じです。

_DSC9293 _DSC9306 _DSC9308 _DSC9326

 

JOnsenは全部英語なのでなかなかつらいのですが、それでも日本人ばかりなのでスピードはゆっくり。

ネイティブの人たちばかりだとスピードも速いし、話している間もかぶせてくるので、なかなか議論に参加するだけでもたいへん。その点、JOnsenはゆったりしているし、ちゃんと意見を言い終わるまで待ってくれるし、英語でのディスカッションに慣れていない身としては、とてもありがたいです。

今年はいつものメンバーになってしまったのはしかたないですけど、来年はぜひ他の人も参加してほしいなぁ。

というわけで、最後に記念写真を撮っておしまい。

_DSC9333

2023/03/21

JEPでは語れないJava 20

このエントリーをはてなブックマークに追加

毎度おなじみ半年ぶりのJavaのアップデートです。Javaのバージョンもとうとう20台になってしまいました。

OpenJDK的には関係ないのですが、Oracleをはじめとした多くのディストリビューションではJava 21をLTSとするため、Java 20でPreviewやIncubatorのJEPに区切りをつけたいところです。

Java 20のJEPは以下の通り。すべてがPreviewもしくはIncubatorです。

  • 429: Scoped Values (Incubator)
  • 432: Record Patterns (Second Preview)
  • 433: Pattern Matching for switch (Fourth Preview)
  • 434: Foreign Function & Memory API (Second Preview)
  • 436: Virtual Threads (Second Preview)
  • 437: Structured Concurrency (Second Incubator)
  • 438: Vector API (Fifth Incubator)

このままでいくと、Virtual ThreadsやVictor API、Foreign Function & Memory APIはJava 21で正式にリリースということになりそうです。switch式のパターンマッチングやレコード型のパターンマッチングもたぶん正式リリースになると思います。

間に合わなかったのが、JEP 429 Scoped Valuesです。

Scoped ValuesはVirtual Threadsと同じくProject Loomで策定されており、ThreadLocalクラスの代わりに使えるAPIです。これが入らないとすると、Virtual Threads的にはちょっとつらいですね。

 

まぁ、JEPの機能についてはだれか書いてくれると思うので、いつものごとくJEPには触れられていないAPIの変更について紹介していきます。

今回も、java.baseモジュール以外のモジュールの変更は少ないし、普通の開発ではほぼ使われないAPIなので、今回はjava.baseモジュールだけ説明します。

 

廃止になったAPI

Java 19と同じく、Java 20でも廃止になったAPIはありません。

 

廃止予定のAPI

Java 20で追加された廃止予定のAPIは、JMX Remote関連のクラスです。

Java 20で、JMX Remoteのシリアライズ、デシリアライズの仕様が変更になったのですが、それに伴うことのようです。ただし、このクラスがなくなったとしても、JMXの使い方に変更はないはずです。

 

クラス

前述したようにJMX Remote関連のクラスがforRemoval=trueになりました。

  • javax.management.loading.MLet
  • javax.management.loading.MLetContent
  • javax.management.loading.MLetMBean
  • javax.management.loading.PrivateMLet

 

例外

Threadクラス関連の例外がforRemoval=trueになりました。

  • java.lang.ThreadDeath

例外名にExceptionともErrorともついていませんが、ThreadDeath例外はエラーの1つです。

ThreadDeath例外はThread.stopメソッドがコールされた時にスローされる例外です。Thread.stopメソッドがJava 18でforRemoval=trueになったので、合わせてforRemoval=trueになったのだと思います。

 

追加されたAPI

Java 20で追加されたAPIの半分ぐらいはFFM APIですが、ここでは省略します。

 

java.base/java.langパッケージ

毎度のことですが、Java 20でサポートされるUnicodeのバージョンが15になったことに伴い、Character.UnicodeBlockクラスとCharacter.UnicodeScriptクラスの定数が増えています。その他は細かい変更のみです。

 

Character.UnicodeBlockクラス

Unicode 15.0で導入されたブロックが定数として追加されています。

  • ARABIC_EXTENDED_C
  • CJK_UNIFIED_IDEOGRAPHS_EXTENSION_H
  • CYRILLIC_EXTENDED_D
  • DEVANAGARI_EXTENDED_A
  • KAKTOVIK_NUMERALS
  • KAWI
  • NAG_MUNDARI

CJK_UNIFIED_IDEOGRAPHS_EXTENSION_Hは漢字ですが、ちょっと見た限り、日本で使う漢字はないようですね。

 

Character.UnicodeScriptクラス

Unicode 15.0で導入されたスクリプトが定数として追加されています。

  • KAWI
  • NAG_MUNDARI

 

Classクラス

後述しますが、リフレクション関連でアクセス修飾子やモジュール関連のフラグを定義したAccessFlag列挙型がJava 20で導入されました。

ClassクラスではクラスのAccessFlag列挙型のセットを返すメソッドが追加されました。

  • Set<AccessFlag> accessFlags()

どういう値が返るのか、JShellで試してみました。

jshell> String.class.accessFlags()
$1 ==> [PUBLIC, FINAL, SUPER]

jshell> List.class.accessFlags()
$2 ==> [PUBLIC, INTERFACE, ABSTRACT]

jshell> ArrayList.class.accessFlags()
$3 ==> [PUBLIC, SUPER]

jshell>

 

Floatクラス

Javaのfloatは4バイト、倍制度のdoubleは8バイトで表されます。これに対しIEEE 754には2バイトで表される半精度浮動小数点数が定義されています。

その内訳は符号が1ビット、指数部で5ビット、仮数部で10ビットとなります。

この半精度浮動小数点数とfloatの変換メソッドがFloatクラスに追加されました。

  • static float float16ToFloat(short floatBinary16)
  • static short floatToFloat16(float f)

float16ToFloatメソッドが半精度小数点数からfloat、floatToFloat16がfloatから半精度浮動小数点数への変換を行います。

Javaで2バイトで表される型といえばshortです。そこで、shortを使って半精度浮動小数点数を表します。なので、shortの値として見てしまったら、全然わからないですw

 

ここに紹介した以外にも、ModuleクラスやModuleLayer.Controllerクラスにメソッドが追加されていますが、これらはFFM APIに関連したPreview APIなので、ここでは省略します。

 

java.base/java.lang.constantパッケージ

java.lang.constantパッケージはinvokeDynamic関連でエンティティのNominal Descriptionを定義しているパッケージです。普通はあまり使わないと思いますけど、indyを使って動的に最適化したい時ぐらいですね。

 

ClassDescクラス

クラスのNominal Descriptionを表すClassDescクラスでは、ファクトリメソッドが1つ追加されました。

  • static ClassDesc ofInternalName(String name)

これまでのファクトリメソッド、たとえばofメソッドの引数はクラス名をピリオド区切りで指定しました。たとえば、Stringクラスであればjava.lang.Stringです。

これに対しofInternalNameメソッドはクラスの内部表現で表します。Stringクラスであればjava/lang/Stringとなります。

この内部表現に関してはJVM SpecのJVMS 4.2.1に記載がありますので、参考になさってください。

jshell> import java.lang.constant.*

jshell> ClassDesc.of("java.lang.String")
$2 ==> ClassDesc[String]

jshell> ClassDesc.ofInternalName("java/lang/String")
$3 ==> ClassDesc[String]

jshell>

 

java.base/java.lang.moduleパッケージ

java.lang.moduleパッケージでも、Classクラスと同様に各クラスにaccessFlagsメソッドが追加されています。

 

ModuleDescriptorクラス/ModuleDescriptor.Exportsクラス/ModuleDescriptor.Opensクラス/ModuleDescriptor.Requiresクラス

前述したようにaccesFlagsメソッドが追加されました。

  • Set<AccessFlag> accessFlags()

 

java.base/java.lang.reflectパッケージ

リフレクションのためのjava.lang.reflectパッケージですが、すでに出てきているAccessFlag列挙型が追加されています。また、それに関したクラスやメソッドも追加されました。

AccessFlag列挙型

AccessFlag列挙型はアクセス修飾子などを定義しています。これらの定数はクラスファイルのヘッダのaccess_flagsで指定するACC_X (XにはPUBLICなどのアクセス修飾子などが入ります)に対応しています。

定数はPUBLICやSTATIC, INTERFACEなど23種類ありますが、量が多いのでここでは省略します。そういうものがあると知って入れば十分だと思います。

メソッドも7種類定義されていますが、まぁ使うことはないでしょう。

 

AccessFlag.Location列挙型

AccessFlagが修飾している対象を表す列挙型がAccessFlag.Location列挙型です。

定数はCLASS, FIELD, MODULEなど9種類があります。

 

ClassFileFormatVersion列挙型

この列挙型も新しく導入された列挙型ですが、似たような列挙型はすでにありました。それが、ソースファイルのバージョンを示すjavax.lang.model.SourceVersion列挙型です。

ClassFileFormatVersion列挙型はクラスファイルのバージョンを示す列挙型です。

しかし、使用している定数はSourceVersion列挙型と同じで、RELEASE_1などRELEASE_の後にバージョンの数字が入る形式になっています。

 

Executableクラス/Fieldクラス/Memberクラス/Parameterクラス

この4クラスも各クラスにaccessFlagsメソッドが追加されました。

  • Set<AccessFlag> accessFlags()

 

java.base/java.netパッケージ

URLクラスにメソッドが1つ追加されました。

URLクラス

URLクラスではファクトリメソッドが追加されています。また、それに伴いコンストラクタがDeprecatedになりました。forRemoval=trueではありませんが、近い将来削除される可能性があるため、今後は使うのを控えた方がいいと思います。

  • static URL of(URI uri, URLStreamHandler handler)

基本的にはURLはURIから作るわけですが、通常はURI.toURLを使います。

ところで、今までのURLオブジェクトをコンストラクタで生成するにはURLStreamHandlerクラスが使われます。そして、URLのパースや検証が実際に使われる時まで遅延されることがあります。

URLのコンストラクタと同様に生成するときにofメソッドを使用すればいいということです。

 

java.base/java.nio.channelsパッケージ

FileChannelクラス

FileChannelクラスはmapメソッドを使用してファイルのコンテンツをメモリにマップすることができますが、FFM APIを使ったmapメソッドも導入される予定です。今はPreview APIになっています。

 

java.base/java.nio.file.spiパッケージ

FileSystemProviderクラス

FileSysstemProviderクラスはファイルシステムの各種操作を行うためのクラスで、FIlesクラスのメソッド群の処理はこのクラスに委譲されます。

逆にいうと、Filesクラスでファイルに対する操作のメソッドはFileSystemProviderクラスにもあるということです。たとえば、FilesクラスのcreateDirectoryメソッドは次のようになっています。

public static Path createDirectory(Path dir, FileAttribute<?>... attrs)
        throws IOException {
    provider(dir).createDirectory(dir, attrs);
    return dir;
}

providerメソッドがFileSystemProviderオブジェクトを取得するためのメソッドです。

このFileSystemProviderクラスに2つ追加されました。というか、何でこのメソッドがなかったのだろうかという感じです。

  • boolean exists (Path path, LinkOption... options)
  • <A extends BasicFileAttributes> A readAttributesIfExists(Path path, Class<A> type, LinkOption... options)

メソッド名の通り、ファイルの有無をチェックするメソッドと、アトリビュートがあれば読み込むメソッドです。

 

java.base/java.textパッケージ

APIの追加はないのですが、BreakIteratorクラスの動作が変更されています。

BreakIteratorクラス

BreakIteratorクラスは文字列の境界の位置を探索するためのクラスです。内部的にはCharacterIteratorインタフェースを使用して文字列をスキャンしていきます。

Java 19の JEPで語れない の中でフライングしてしまったのですが、このBreakIteratorクラスの動作が変更されました。

具体的には、Java 19までのBreakIteratorクラスではUnicodeのゼロ幅接合子などの境界を正しく検出することができませんでした。

BreakIteratorクラスが使えなかったので、ゼロ幅接合子などを含む文字列を分割するには正規表現の\b{g}を使うしかありませんでした。

これに対し、Java 20ではゼロ幅接合子なども正しく扱えるようになっています。

文字の境界に関してはUnicode Standard Annex #29の Unicode Text Segmentation を参照してください。

 

java.base/java.util.concurrentパッケージ

Fork/Join Framework関連クラスでAPIが追加されました。

 

ForkJoinPoolクラス

タスクを登録するsubmit関連のメソッドが追加されました。

  • <T> ForkJoinTask<T> externalSubmit(ForkJoinTask<T> task)

ForkJoinPoolでプールしているワーカースレッドではない外部のスレッドからタスクを登録するためのメソッドです。

submitメソッドはForkJoinPoolのワーカースレッドでも外部のスレッドでも、どちらからでも登録できます。外部のスレッドから登録した場合はexternalSubmitメソッドと同じ動作です。

なので、なぜ今になってexternalSubmitメソッドが追加されたのかイマイチよく分からないのです。

 

ForkJoinWorkerThreadクラス

Fork/Join FrameworkはタスクスケジューリングにWork-Stealingを使用しています。Work-Stealingでは各ワーカースレッドがタスクキューを持ち、キューの先頭のタスクから実行を行います。

もし、自身のタスクキューが空の場合は、他のワーカースレッドのタスクキューの最後からタスクを取り出して(盗んで)実行します。

ForkJoinWorkerThreadクラスではこのタスクキューに登録されているタスクの個数を返すメソッドが追加されました。

  • int getQueuedTaskCount()

 

java.base/java.util.regexパッケージ

java.util.regexパッケージのAPI追加は普通の開発者にとって唯一役に立つAPIのような気がしますw

 

MatchResultインタフェース

MatchResutlインタフェースを直接使用することはないとは思いますが、MatcherクラスがMatchResultインタフェースを実装しているので意識はしていなくても使用しているはずです。

MatchResultインタフェースでは5つのメソッドが追加されました。すべてがデフォルトメソッドなのですが、namedGroupsメソッドとhasMatchメソッドはUnsupportedOperationException例外をスローする実装になっています。

だったらデフォルトメソッドにしないでほしいですけど、すでにリリースされていたインタフェースにメソッドを追加するとなるとこうするしかないんでしょうね。

  • default int end(String name)
  • default String group(String name)
  • default boolean hasMatch()
  • default Map<String, Integer> namedGroups()
  • default int start(String name)

endメソッド、groupメソッド、startメソッドは正規表現のグループに関するメソッドです。

この3種類のメソッドの引数の型はすべて文字列です。引数名のnameは正規表現の名前付きグループに対する名前となっています。

実をいうと、これらのメソッドはJava 19まではMatcherクラスで定義されているメソッドでした。それらが、MatcherクラスではなくMatchResultインタフェースで定義されることになったわけです。

引数のないメソッド、およびint型が引数の型のメソッドはMatchResultインタフェースで定義されていたので、すべてMatchResultインタフェースのメソッドということになりました。

namedGroupsメソッドも正規表現のグループに関連するメソッドです。

名前付きグループを使用した場合、名前とグループ番号を対応づけたマップを返します。名前を使用していない場合は空のマップが返ります。

JShellで動作を確認してみます。

jshell> var pattern = Pattern.compile("(?\\d+)(?[a-z]+)(?\\d+)")
pattern ==> (?<prenum>\d+)(?<alphabet>[a-z]+)(?<postnum>\d+)

jshell> var matcher = pattern.matcher("12ab345e67fgh890i")
matcher ==> java.util.regex.Matcher[pattern=(?<prenum>\d+)(?< ... +) region=0,17 lastmatch=]

jshell> matcher.find()
$3 ==> true

jshell> matcher.namedGroups()
$4 ==> {prenum=1, postnum=3, alphabet=2}

jshell> matcher.group("alphabet")
$5 ==> "ab"

jshell> matcher.group(2)
$6 ==> "ab"
	  
jshell> matcher.start("postnum")
$7 ==> 4
	  
jshell> matcher.start(3)
$8 ==> 4

jshell> matcher.end("prenum")
$9 ==> 2

jshell> matcher.end(1)
$10 ==> 2

jshell>

数字 小文字アルファベット 数字 と連なるパターンを作ってみました。グループは()で、グループ名は?<>で表します。

したがって、グループ名がprenumばグループ1、名前がalphabetはグループ2、postnumがグループ3になります。

namedGroupsメソッドを使用すると、この関係を持ったマップが返ることを確認できます。

今までグループの個数はgroupCountメソッドで分かりましたが、名前一覧は取得できなかったので名前付き正規表現が扱いやすくなるはずです。

 

最後のhasMatchメソッドはfindメソッドなどを複数回使用してマッチさせた後でもマッチしていたかどうかを返すメソッドです。もちろん、過去だけでなく現在の状態も加味して結果を返します。

 

Matcherクラス

MatcherクラスはMatchResultインタフェースを実装しているので、デフォルトメソッドのnamedGroupsメソッドとhasMatchメソッドをオーバーライドしています。

使用例はMatchResultインタフェースで示した通りです。

 

Patternクラス

PatternクラスでもnamedGroupsメソッドが追加されました。

  • Map<String, Integer> namedGroups()

PatternクラスではMatcherオブジェクトを内部的に使用して正規表現にマッチしているかどうかを調べることができます。そんな場合に使用します。

とはいうものの、あまりこういう使い方はしないような気がするんですよね...

 

java.base/javax.net.sslパッケージ

 

SSLParametersクラス

SSLParametersクラスはSSLというかTLS接続のパラメータを扱うためのクラスです。

SSLSocketオブジェクト、SSLServerSocketオブジェクトもしくはSSLEngineオブジェクトから取得します。

SSLParametersクラスではキー交換時に使用する名前付きグループに関するメソッドが2種類追加されました。

  • String[] getNamedGroups()
  • void setNamedGroups(String[] namedGroups)

 

 

Java 20のAPI変更について紹介しましたが、意外に少ないですね。しかも、普通に使いそうなAPIは正規表現のAPIぐらいのような気がします。

Java 20のAPI変更は少ないですけど、Java 21はjava.utilパッケージのコレクションにメソッドが多く追加される予定です(JEP 431)。

JEP 431: Sequenced CollectionsはPreview JEPではなくStandard JEPなので、そのまま正式リリースされる予定です。Java 21の時のこのシリーズはJEPには含まれていますけども、JEP 431も含めて紹介するつもりです。