SpringBootでAsyncを使う時に知っておきたいExecutorのこと
- SpringFramework 6.0.11
- SpringBoot 3.1.2
@Async
と @EnableAsync
の使い方
SpringFrameworkで @Async
を使うとかんたんにメソッドを非同期で実行できます。
@Component class AsyncComponent { @Async void method() { // 時間のかかる処理 } }
使う側は単にメソッドを呼び出すだけです。戻り値を処理したいなら Future
で受ければいいけど、投げっばでいいなら void
で良い。投げっばでいい場合の方が多いはず。
@Async
を使うためにはアノテーションを処理する何かしらが必要で、この一式を用意するように指示するのが @EnableAsync
です。
以下のようなクラスをSpringがわかるところに置いてあげます。
@EnableAsync @Configuration(proxyBeanMethods = false) class AsyncConfiguration { }
かんたんですね。
⚠️以降はひたすらSpringの中身の話です。「知っているべき」とまでは言いませんが、知っておくとなんかの役には立つんじゃないかな。
SpringFrameworkの話
細かいことはコードやおまけでつけたクラス図と照らし合わせてもらうとして、大雑把に以下のようなことをやっている。
- 準備
@EnableAsync
によりAsyncAnnotationBeanPostProcessor
が登録される。AsyncAnnotationBeanPostProcessor
は@Async
なメソッド呼び出しをAnnotationAsyncExecutionInterceptor
でラップするようにする。
- 実行
@Async
なメソッドが呼び出されるとAnnotationAsyncExecutionInterceptor
がメソッドに応じたExecutor
に実行させする。
コードを見ての通り、 AnnotationAsyncExecutionInterceptor
では Executor
に Callable
を submit
しているだけです。( Executor
や Callable
も java.util.concurrent
です。わからない場合は別途勉強してください。)
さてここで重要になるのが Executor
の決まり方です。
Executor
は AnnotationAsyncExecutionInterceptor
(の親である AsyncExecutionAspectSupport
)により以下の順で決定されます。
- メソッドで指定されている
Executor
のBean AnnotationAsyncExecutionInterceptor
に設定されているデフォルトのExecutor
TaskExecutor
のBean"taskExecutor"
という名前のExecutor
のBeanSimpleAsyncTaskExecutor
を生成して使用
色々ありますが、SpringFrameworkの歴史的経緯などもあるのでしょう。とりあえずこうなっています。
SpringFrameworkの文脈で「 @Async
を動かす Executor
に手を加えたい」と思ったならば、この1から4のいずれかを使えばいいわけです。(5は全部ダメだった時にSpringがなんとか動かすための実装なので手は出せません。)
1番目はたとえば @Async("myExecutor")
と書いていれば myExecutor
という名前の Executor
Beanがあればそれが使われるということです。アノテーションを使わない場合も考慮されてるのでこんな表現になっています。
2番目の「 AnnotationAsyncExecutionInterceptor
に設定されているデフォルトの Executor
」はわかりづらいですが、 @EnableAsync
を使用する場合は AsyncConfigurer
の getAsyncExecutor
メソッドが返すインスタンス になります。なぜかよく紹介されているので使ったことのある人も多いかもしれません。
SpringBootの話
SpringBootを使用する場合、3番目の「 TaskExecutor
のBean 」が使用されます。
このBeanは TaskExecutionAutoConfiguration
が作成 します。
自動構成される TaskExecutor
は他の Executor
のBeanを作成すれば作られなくできますが、嬉しいことはないのでやめておきましょう。
TaskExecutionAutoConfiguration
を見ると、 TaskExecutorBuilder
を通して TaskExecutionProperties
に設定された値などを使って TaskExecutor
を作っているのがわかると思います。
SpringBootを使用する場合、SpringBootのプロパティ(ここでは spring.task.execution.*
)が効くことが期待されます。
そのため、 @Async
を実行するデフォルトの Executor
を差し替える場合、以下の対応は好ましくありません。動くけど。動くけど、好ましくない。
AsyncConfigurer
を作成してExecutor
のインスタンスを生成する(前述2番)TaskExecutorBuilder
(もしくはTaskExecutionProperties
)を使用しないTaskExecutor
のBeanを作成する(前述3番)taskExecutor
という名前のExecutor
Beanを作成する(前述4番)
……「全滅じゃん?」と言ってはいけない。まだだいじょうぶ。
下2つを採用する場合は spring.task.execution.*
が効かないことを明確にドキュメントに記述し、保守では引き継ぎ続けなければなりません。また「自分が作った通りの Executor
」となるため、SpringBootが「だいたいのアプリはこの設定でいけるっしょ」とやってくれているものに乗っかれません。ちゃんと自分たちで決めてぜんぶ実装しなければならないのです。もちろんSpringBootの設定値も妥当性を確認するのが望ましいですが、全部の値は見てられないし、そんな関心のないものはSpringBootに任せとくというのも一つの選択です。「SpringBootではないデフォルトでもいいじゃないか」というのもそれはそうかもしれませんが、おそらくSpringBootよりも広いコンテキストのデフォルトであり、SpringBootのデフォルトよりも微妙なものが多いなぁという感覚。
一番上の「 AsyncConfigurer
で Executor
を作成」はプロパティの問題に加えてさらに別の問題があります。
Executor
をBean登録しないので、SpringBootの管理外となってしまいます。SpringBootが管理してくれないことによる問題としては、シャットダウン時に後片付けがされないことや、メトリクスが自動では見えないことが挙げられます。
SpringBootActuatorで metrics
エンドポイントを公開すると /actuator/metrics
で簡易的なメトリクスが参照できます。
SpringBootActuatorは Executor
なBeanを TaskExecutorMetricsAutoConfiguration
が拾って自動的に見れるようにしてくれます。以下ではアクティブなタスク数とキューに入っている数を取得しています。
% curl http://localhost:8080/actuator/metrics/executor.active -s | jq { "name": "executor.active", "description": "The approximate number of threads that are actively executing tasks", "baseUnit": "threads", "measurements": [ { "statistic": "VALUE", "value": 8 } ], "availableTags": [ { "tag": "name", "values": [ "applicationTaskExecutor" ] } ] } % curl http://localhost:8080/actuator/metrics/executor.queued -s | jq { "name": "executor.queued", "description": "The approximate number of tasks that are queued for execution", "baseUnit": "tasks", "measurements": [ { "statistic": "VALUE", "value": 52 } ], "availableTags": [ { "tag": "name", "values": [ "applicationTaskExecutor" ] } ] }
実運用では /actuator/metrics
ではなく /actuator/prometheus
などを使うと思います。
% curl http://localhost:8080/actuator/prometheus -s | grep "^executor_" executor_pool_max_threads{name="applicationTaskExecutor",} 2.147483647E9 executor_active_threads{name="applicationTaskExecutor",} 8.0 executor_pool_size_threads{name="applicationTaskExecutor",} 8.0 executor_completed_tasks_total{name="applicationTaskExecutor",} 168.0 executor_queued_tasks{name="applicationTaskExecutor",} 14.0 executor_queue_remaining_tasks{name="applicationTaskExecutor",} 2.147483633E9 executor_pool_core_threads{name="applicationTaskExecutor",} 8.0
これが見えないのは運用を考えると厳しいため、AsyncConfigurer
を使う場合も別途Bean登録した Executor
を使うようにしてあげる必要があります。で、そうするなら TaskExecutor
なBean登録すりゃいいんじゃね?などになり、であれば AsyncConfigurer
をSpringBootで使うのは微妙に思えます。
そもそもやりたいこと
Executor
を変えたいというのはそうそうありません。
もちろん ThreadPoolTaskExecutor
だと要件を満たさなくて、別のライブラリや自分たちで制御する Executor
にしたいなどもあるでしょう。その場合はやるといいと思います。
ですがここに手を伸ばしたい多くの理由は「スレッドを跨いでMDCなど ThreadLocal
に頼っているコンテキストを引き渡したい」などでしょう。
であるならば Executor
の置き換えではなく、 TaskDecorator
を使うのが妥当でしょう。 TaskExecutionAutoConfiguration
の taskExecutorBuilder
メソッド を見てください。
引数で ObjectProvider<TaskDecorator>
を受けて
public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties,
ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
ObjectProvider<TaskDecorator> taskDecorator) {
getIfUnique
で取得したのをTaskExecutionBuilder
に渡しています。
builder = builder.taskDecorator(taskDecorator.getIfUnique());
このため TaskDecorator
をBean登録すれば自動的に取り込まれますが、 ObjectProvider#getIfUnique
の動きも知っておきたいところです。
- 対象Beanがなければ
null
- 対象Beanが1つだけならそれ
- 対象Beanが複数ある場合
- 何かしらで1つに特定できればそれ
- 特定できなければ
null
注意したいのは「複数登録されていると null
になり得る」です。特定にはBeanの名前や @Primary
が使用されますが、特定できなくてもエラーも出ず単に設定されていないって動きをします。Bean定義が散在すると気づかないうちに未設定になっているなどがあるかもしれません。残念ながら特にエラーログも出てくれません。組織体制によっては固く定義した方がいいかもしれません。
ということで「 Executor
とかいじってないで TaskDecorator
だけBean登録する」で多くの場合は事足りるはずです。
@Bean TaskDecorator taskDecorator() { return runnable -> { // ここは呼び出し元のスレッドで実行される。必要なものを取り出す。 return () -> { // ここからExecutorのスレッドで実行される。外から受け取ったものを設定する。 try { runnable.run(); } finally { // 後片付けを忘れない } }; }; }
もし「TaskDecorator
じゃ足りないんだよぅ」とかでも、先のシグネチャの通りまだ TaskExecutorCustomizer
があります。 TaskExecutor
をごそっと差し替えるのは、以下を検討してからって感じですね。
- プロパティでなんとかならないか
TaskDecorator
でどうにかならないかTaskExecutorCustomizer
でもできないだろうかTaskExecutorBuilder
は使えるんじゃないか
どれでも満たせないなら Executor
のBean登録で対応。その時は Executor
をちゃんと正しく取り扱いましょう。でもそこまで行くと @Async
とか使わず実装する方が制御しやすいとかもある気がしないでもないです。
あとがき
SpringBootはSpringFrameworkの拡張ポイントも踏襲しているため、一つのことでもたくさんのやり方があります。 検索するとやり方はたくさん出てくるし、確かにそれでも動きはします。確かに動く、動くけど、けどさー……と思いつつ。
「それでも動くけどでもそれはそれのために使うものじゃなくて、いやそれでも動くんだけどさ、動くけど……まぁいいか……」
— irof (@irof) 2023年8月21日
あの時「まぁいいか」って言ったやつ誰だ(過去の自分
「知っているべき」って言わないのは、私も知らずにやってるものたくさんあるからです。知ろうと思った時に知ることができる筋力はつけておきたいお気持ち。
特にSpringFrameworkの拡張ポイントはSpringBootよりもドラスティックに変更できるものも多く、SpringBootはそちらに譲る形で実装されています。たとえば @ConditionalOnMissingBean
とか。
でもSpringBootにのっかるならSpringBootに乗っかったまま拡張したい感がある。SpringBootのバージョンアップとかでその辺の対応がいらなくなった時にすんなり乗れますし、互換性も崩れにくいし、マイグレーションガイドでフォローされる可能性も高い。先にあげたメトリクスのようにいい感じに面倒見てくれるところも多いですし。たまに予期しないところに影響でたりもするんだけど、それ系は自分が微妙なことしたりするせいで、メリットの方が多いと思ってます。
あ、今回は @Async
のを書きましたが、 @Scheduled
の Executor
も大体同じです。 ThreadPoolTaskExecutor
と ThreadPoolTaskScheduler
の違いは型階層だと TaskScheduler
を実装してるかどうかだけで、TaskSchedulingAutoConfiguration
が @AutoConfiguration(after = TaskExecutionAutoConfiguration.class)
なところとか趣深い。
おまけ
この辺のことを整理するときは物理紙やMiroとかに図を起こしながらやってます。こういう崩したり強弱つけたり配置に意味を持たせたりは機械的なクラス図だと厳しいのよね。
参考/参照
- Spring Boot Reference Documentation / Core Features / 7. Task Execution and Scheduling
- Tipにそれっぽいこと書いてるけど、これわかってから読むとわかる気がしないでもな……いややっぱわからん。わからず読んでわかる気はしない。
- Springのドキュメントの探し方 - 日々常々
まぁ「コードに書いてある」も「どこかにある」に含めちゃってたりする
- 今回はほぼコードですね。
- 誤解しがちなThreadPoolTaskExecutorの設定 - IK.AM
- 設定値の話。メトリクス眺めながら設定いじって動かすと「なるほどー」となると思います。
-
- この辺の話は書いてなさそう(目次、索引、Asyncの検索とかに引っかかってこない)
@Async
はそんな頻出でもないですし、守備範囲としては入ってなくていいと思います。プロになった後に理解しましょう。
-
- SpringFrameworkなところは大体書いてる。
TaskDecorator
はSpring4.3導入なのでないっぽい。 - SpringBootは1時代なので、まぁ。
- SpringFrameworkなところは大体書いてる。
「コーヒーの味はわかんないけどなんとなく豆挽いて手で淹れるのがいいなぁ」
ぶっちゃけコーヒーの銘柄とか覚えてないけど、なんとなく好きだなーいまいちだなーとかはある。
あ、ブルーマウンテンだけはわかる気がする。あんま好きじゃない方に属するけど、飲む飲まないで言うと飲む感じ。
その程度の味覚だけど「毎日一人分のコーヒーを豆挽いて淹れたい」って欲求を満たしてる私の一式を晒しちゃおうという内容です。 雰囲気でやるならこれで十分で、こだわる人には役に立たない内容だし、ちゃんとしたい人はちゃんと調べてね。
道具
- 豆を挽くもの
- 湯を沸かすもの
- 重さはかるもの
- 淹れるもの
- (時間はかるもの)
- (温度はかるもの)
大雑把に前4つあればOKです。時間と温度は雰囲気で。
コーヒーメーカーで全部揃うよとか色々あるとは思うんですけど、「豆から手で淹れる」ので。コーヒーメーカーは一杯分だけ淹れ用と思うと微妙で使わなくなっちゃったのよね。
豆を挽くもの
「粉買えばいいじゃん」って思うかもだけど、ゴリゴリ挽きたいから豆です。味の違い?そんなの私の舌にはわからないよ……いや区別はつくんだけど、味の記憶がどうも苦手で「前に飲んだ粉と比べるとどう?」とかならなくて。でも酸化の仕組みとかは知ってるから表面積が大きい粉の方がダメになるの方が早いの当たり前じゃんって思うし。あとやっぱ豆挽く手応えが楽しいよね、ゴリゴリ。
最近使ってるのがTIMEMOREの赤いの。1人分だと少量だと両手に持ってぐるぐるできるのがいい感じです。
これ買うときに同じ使い方できるHARIOのと迷いました。
「赤い金属の見た目が格好いい」だけの理由でTIMEMOREのにしました。機嫌良く使ってます。静電気がすごいので対策しないとびっちりついちゃいます。
RDTとかちゃんと対策すれば大丈夫。まぁ使い方です。
欠点があるとしたら1回で20gくらいまでしか入らないところで、2杯以上を一度に淹れたいときには面倒かもです。淹れないから困らないけど。
TIMEMOREの買う前に使ってたのはよくある形のこれ。
机とか台の上で使わないといけなくて、押し付けながらしないといけないのよね。量が多いとこれでいいんだけども。 あと細かさを変えるのがハンドル自体外さないといけなくて面倒なとこだったりする。TIMEMOREのは裏のツマミを回すだけでいけるから楽。
電動のもいくつか買ったんだけど、一人分なら手動で十分です。湯を沸かしながら挽くとちょうどな感じ。
電動のを選ぶとしたら考えること
- プロペラのはダメ。早いし安いけど、回してる時間だけで細かさを調整する感じになるので、均一になるわけもなく。
- バッテリーのはダメ。挽きたいときに電池切れとかストレスでしかない。
- 一人分なら要らんよ。
詳しい話
なんちゃら式とか微粉とか、こだわる人には大事なものが色々あるんだと思います。 こだわる人はちゃんと調べましょうね!私は興味ないや!!(一通り調べたり試したりしたけど、違いに興味が持てなかったです。わかる舌か味覚記憶も私にはなさそうだし。)
湯を沸かすもの
どんなのでもいいんだけど、注ぎ口が細いやつ。
これじゃないけどこれっぽいのを使ってます。そんなこだわりはない。
電気ケトルはプラスチックのを買ってみたら樹脂の匂いがひどくて、重曹とか色々やったけど全然取れなかったので手放しました。 なので金属のを使ってます。なんか溶接甘いのか水漏れしたりするんだけど、まぁいいかなって。
注ぎ口が細くないので「これでいいや」って思ってたけど、細いのを使ってみたら全然淹れやすさが違ったので、絶対注ぎ口が細いやつを使うのがいいです。
コーヒーには使ってないけど半年くらい前に買ったの。
1.5リットル沸かせて煮出しもできるので、毎日のお茶入れに重宝してます。これは買ってよかった。
重さはかるもの
これは1g単位ではかれるならなんでもいい。
1kgまでしかはかれないから、他のものに使おうとたまにEEEEになってイラっとするんだけど、コーヒー淹れる文脈だと困ることはないです。 コーヒー用だとタイマーもついてる秤はあるけど、タイマーは別に百均のでもスマホでもいいし、ついてる必要はないと言うか。
淹れるもの
HARIOのみっつを気分と豆で使い分け。
定番かな。
「ペーパーフィルター切れたときにも淹れられるバックアップ」くらいのノリで買ったんだけど、ペーパーフィルターを使わないので油がそのまま出るから、そういうのが好きなタイプの豆だとこれで淹れてます。豆による。フレンチプレスかこれのどっちにするかなーはその時の気分ですね。
ペーパーフィルターで入れたらあんま特徴ねーなって思ってたコーヒーを金属メッシュのでいれたらびっくりするほど主張激しい。……これはぺーぱーでいれよっと。
— irof (@irof) 2023年5月25日
なんとなく「陶器のドリッパーほしいなぁ」と。淹れ方に揺れがないので安定した感じになります。
最近Melittaのも買ってみた。
一つ穴の一回注ぎなので、MUGENと同じく安定します。でも一回注ぎって淹れてて楽しくないんだよね。
今回はハンドドリップの話なので、フレンチプレスはまた気が向いたら。たぶん気は向かないけど。
豆
コーヒーショップで飲んで「いい感じ」と思ったのを買ったりもするんだけど、基本はふるさと納税のランダムで来る定期便です。豆自体にはこだわりないし、結構いい感じのが来てますので。
この辺は使ってるふるさと納税サイトで適当に探してください。(さとふるのもあるけどリンクの取り方がわからなかった。)
パッケージの仕方とか選ばれるのとかまちまちなので、1回か3ヶ月とか短めで試して、気に入ったら1年とかにする感じでやってます。
今月の配給が届いた(コーヒー豆 pic.twitter.com/8WDo6svXzD
— irof (@irof) 2023年4月9日
消費ペースに合わせて届くようにしてると良い。
消費できない量が届くとせっかく焙煎直後に送ってもらえるのに勿体無いのでギリギリ狙うのだけど、たまに足りなくなります。
そろそろコーヒー豆届かないと禁断症状でてしまう
— irof (@irof) 2022年8月5日
ふるさと納税はすぐに届かないので、こうなったら豆を買いに行くことになる。カフェイン中毒になる程飲んではないと思うんだけど、切れると頭痛する。気がする。
コーヒー豆の保管
「冷凍庫がいい!」「いや冷蔵庫!」「常温では?」
色々あるけど、だいたい「長期保管はこうしてね」って書いてるので、そのやり方に従ってます。焙煎した人が言うんだからそれを支持。 どれも届いたらすぐに使いたいけど、毎週受け取るとかに調整するのも面倒だしねぇ。
淹れ方
淹れ方は動画とか見て「ほーん」って言って、結局はてきとーにやってる。
道具変えた後とか新しい豆とかは温度はかったり時間はかったりするけど、だいたいこんな感じかーってなってからはほんと適当に。
ポットとか使わず直接マグカップです。洗い物面倒だし。
あ、淹れる時もポットとかじゃなくマグカップに直です。洗い物めんどいし。取手は落としたら砕けたのを接着剤でつけた! pic.twitter.com/8jYPIqRYKQ
— irof (@irof) 2023年6月10日
先ほどの秤の上にマグカップ置いて、ドリッパー置いて、粉入れて、で重さ見ながら湯を注ぐ。これで湯量は安定するので、味も概ね安定します。
電気ケトルに入れる水の量が多くなりすぎると沸かす時間が無駄にかかるしエコじゃないので、ざっくり計量カップではかってます。
別に計量カップは百均のとかでもいいんだけど、なんとなく「ガラスの計量カップって格好よくね?」くらいで買いました。 地味に重い(420gある)ので1kgの秤だと不安になりますが、300ml弱しか入らないしセーフ。 ちなみに湯を沸かすための大雑把な量だけなので、目盛は見てない。……コップでいいじゃん。
まとめ
「豆を挽いて手で淹れる」のが好きです。 コーヒーは淹れてる時の匂いが一番美味いと思います。 豆によっては挽いた直後の華やかな香りも美味しい。
コーヒーの味は「好きな感じ」「好きではない」「ミスった」くらいの区別しかできないからこれで十分だと思ってる。 「ミスった」は三杯飲めって言われると嫌だなーってなるかもだけど、一杯なら飲んじゃうし。
いやうっかり数ヶ月放置しちゃった粉とかは「飲めない」になるけど。
そんな感じ!
Javaのバージョンの取り扱い(2023年6月)
ツイート したらそれなりに反応があったので、少し丁寧に書いておこうかなと。
水物な内容なので、自動でつく投稿日時以外にもタイトルに「2023年6月」を入れて強調しておきます。
しょーとあんさー
よくわかんないならJava17にしておきましょう。
前提
ツイート。だよねーって思ったので、下に書いてたを持ち上げておきます。
LTSとかいう言葉が出てきますが、現在のJavaはメジャーバージョンがLTSと非LTSがあります。 OracleJavaSE を前提にしています。他のサポートも似たり寄ったりな感じと思っているけれど、自分たちが使ってるとこのサポートを確認してくださいまし。
また、本稿は「Javaのバージョン?何それ?」とか「色々あるけど最新使ってたらいいんだよね?」とかそういう方向けで、プロダクトのJavaバージョンを選定する方々向けではありません。そういうのに必要な知識には全然足りません。
選択肢になり得るバージョン
以下のバージョンを識別できていれば十分です。
- Java8
- Java11
- Java17
- (Java20)
- Java21
過去バージョンは目にする機会がなければ識別しなくていいです。
Java8
非常に古いバージョンではありますが、まだまだ多くのシステムで現役で使われています。Java8からJava11の間にはいくらかの技術的な壁があり、おいそれと上げられるものではありません。
現在Java8で動作しているシステムは長い間継続して価値を生み出し続けているものであり、おそらく安定性が求められます。もしくは無い袖は振れないとか。仕方ない。
新規としては採用してはいけないバージョンです。現代のフレームワークやライブラリは使用できませんし、Java8で作ってしまうと以降は手を出せなくなります。よほどのことがあっても採用を避けることを強くお勧めします。
Java11
5年前で少し半端に感じるバージョンですが、Javaのリリースモデルが変わったJava9後の最初のLTSであり、注目が集まった時期でもあります。それまでメジャーバージョンについて行っていた組織が「最新についていこうとしたけど厳しい」と息切れをしたあたりの休息ポイントでもあります。
そんな背景のせいか、Java8ほどでないにせよ結構よく見るバージョンです。「サポートバージョンは11以降」となっているライブラリもそれなりに多いとかなんとか。
新規としては採用してはいけないバージョンです。何気にJava8よりもExtendedSupportが短いという理由で選んではいけないバージョンだったりします。Java17が出てもう2年になりますし、わざわざJava11を選ぶ動機が思いつかないです。
Java11はJava9などの技術的な壁を突破しているため、Java17へのバージョンアップは比較的容易です。比較的。実際のとこ問題がなかったとは言いませんが、解決が困難なものは踏んだ記憶はないです。もしJava11を使用していてアップデートをやったことがないのであれば、軽い気持ちで試してみましょう。うっかりできるかもです。
Java17
現在業務で採用するならこれになると思います。SpringBoot3.0はJava17以降しかサポートしていません。よくわかんないならJava17にしておきましょう。言語機能もいい感じ。
概ねこの通りですね。業務実装でもRecordやText Blockは役立つので是非。先日のJJUG CCC 2023 Springのライブコーディングでも使い倒しました。switch式はenum多用してたらーかな。instanceofはライブラリとか作るなら結構いいんだけど、Java11サポートしておこうと思うと使えなくて悲しみ……。え、Sealed Class?使ってないねぇ。
3ヶ月後にはJava21が出ますが、切り開くモチベーションや熱意が捻出できないなら、フレームワークやライブラリの対応、世間の実績を待つのが無難ではあります。 今年いっぱいはJava17でいいです。これは出た直後にJava21を採用するような方々に向けて書いてませんので。
あ、最後に乗っけてるスライドのように、最新版でのビルドは回しとくことをお勧めします。
Java20
現行最新メジャーバージョン……ではありますが、「最新のJavaを使っておけばいいよね」という気持ちで選んでしまうと要らぬ苦労をしてしまうバージョンです。いや使っててもそんな問題は出ないんですが、何かにハマっても情報が期待できません。情報がないわけではないので情報収集能力がそれなりにあれば大丈夫かもしれません。それなりに関心がなければ読まないであろう「私のブログのこの文章」に辿り着けているので、大丈夫な可能性はそれなりにありますが、貴方だけが情報収集できてもメンバー全員にこれを求めるのは酷でしょう。
もしJava20を採用していてJava17に下げられない事情があるなら、3ヶ月我慢してJava21に進みましょう。
Java20は前述のように「最新バージョンで動作するか」を確認するCI環境や、新しいバージョンを試してみたい熱量ある開発者のローカル環境で使われるバージョンかと思います。
……メジャーバージョンを期間固定でリリースする意義は理解できるのですが、LTS以外はメジャーバージョン扱いされていないのが実情だと思います。とか言うのはJDK開発されている方々に失礼なんだろうな、と思いつつ。。
ごめんて。
Java21
9月リリース予定のバージョンです。 今年の年末、遅くとも来年以降はJava17の新規採用はJava11と同じ理由で「選ぶ理由がない」となり、Java21が選ばれるようになるはずです。
ツイートで「大コケしなきゃ」としているのは、互換です。Javaの互換性を考えるとまず致命的なのはないでしょうけれど、何も問題ないってこともないでしょう。すんなりいけば新規採用はもちろん、現在17を採用しているところでも21にするところは多いと思われます。
ちなみに17と比較して是非21にしたい!って魅力は新しさやJava8よりサポート期間が長い(Java8が長すぎんだよ)ってことくらい。コーディング的にはあんまり嬉しいことはないです。ないこともないけど、どうかなぁ……くらい。
言語機能に大きな差がないので、Java17で開発してJava21でリリース前のテストとかいう青写真はそれなりに現実的だと思います。この辺りはタイミングものですね。
その他のバージョン
趣味でどうぞ。
20はまだ最新メジャーバージョンなので20のとこに書いた理由で使われるのは分からなくはないですが、9,10,12,13,14,15,16,18,19を積極的に選ぶ理由は思いつきません。もし使ってたらすぐに変えることをお勧めします。モブとかを眺めていると18が入ってるのに気づくこともありますが、そんな珍しいことではなかったりします。おそらく開発環境構築時に最新を選んだとかの理由だと思いますが……。
あ、7以前?応援してます。
💪(しばらくみたくない)
ってことで
これくらい抑えておけば十分だとおもいます。
変なこと書いてたらこっそり教えてください。
参考
JDKのディストリビューションの話とかは本をどーぞ。状況は多少変わってるけど、どんな関係になってるのかとかはわかり良いと思います。
JDKのセットアップに関しては過去エントリ。
バージョンアップの話は2015年に話したスライドを見てくださいまし。8年経ってますけど大きく外してはなさそうです。