※当サイトの記事には、広告・プロモーションが含まれます。

Java マルチスレッドでデッドロックの確認どうする?

f:id:ts0818:20201216204101j:plain

shinshizo.com

 これは此世のことならず 死出の山路の裾野なる 賽の河原の物語 聞くにつけても憐れなり 二つや三つや四つ五つ 十にも満たない嬰児が 賽の河原に集りて 父恋し母恋し 恋し恋しと泣く声は この世の声とは事変わり 悲しさ骨身を徹すなり 彼の嬰児の所作として 河原の石を取り集め 是にて回向の塔を積む 一重積んでは父のため 二重積んでは母のため 三重積んでは故里の 兄弟我身と回向して 昼は一人で遊べども 日も入相の其の頃は 地獄の鬼が現れて やれ汝等は何をする 娑婆に残りし父母は 追善作善の勤めなく 只明け暮れの嘆きには むごや悲しや不憫やと 親の嘆きは汝等が 苦患を受くる種となる 我を恨むることなかれ 黒金棒をとりのべて 積みたる塔を押し崩す 其の時能化の地蔵尊 ゆるぎ出させ給いつつ 汝等命短かくて 冥土の旅に来たるなり 娑婆と冥土は程遠し 我を冥土の父母と 思うて明け暮れ頼めよと 幼きものを御衣の 裳の内に掻き入れて 憐れみ給うぞ有難き 未だ歩まぬ嬰児を 錫杖の柄に取り付かせ 忍辱慈悲の御膚に 抱き抱えて撫で擦り 憐れみ給うぞ  有難き

賽の河原で石を積む幼な子・・・地蔵和讃 | 神使像めぐり*余話

⇧「賽の河原」の石積みの詠って、親より子供が早くに亡くなることの罰として科されるって説があるけども、虐待とか不条理な故あって亡くなる子なんかの場合ってどうなるんですかね、どうもボクです。

延々と同じことの繰り返し、それは、言いたいことも言えないこんな世の中じゃポイズン!(『POISON ~言いたい事も言えないこんな世の中は~【反町隆史】』)な現代も変わらないということでしょうかね。

というわけで、今回もJavaについてですかね。

レッツトライ~。

 

何が起きたか? 

事の発端は、マルチスレッドの実装でsyncronaizedなメソッドを試していて、処理が一向に進まないけど、デッドロックとか起きてんのか?って思って、何か調べる方法ないもんかって思ったんですよ。(ちなみにデッドロックではありませんでした...)

スレッドについてなんかは、

java2005.cis.k.hosei.ac.jp

⇧ 上記サイト様の説明でイメージが掴めるかと。

 

一般的にEclipseなんかで「Javaプロジェクト」作成とかで「main」メソッドを持つクラスを作ったデフォルトの状態は「シングルスレッド」と見なせそうです。(「インターフェイス」「クラス」なんかを一切継承してないって前提で)

panda-java.blog16.jp

⇧ 上記サイト様の図がイメージしやすいかと。 

 

 

確認方法

今回はデッドロックでは無かったんだけど、今後のために、処理がむちゃくちゃ遅くてどうなってる?って思う場合がきっとあるだろうと思ったので、調べてみました。

 

software.fujitsu.com

アプリケーションのハングアップや、スローダウンといった現象が発生した場合は、アプリケーションがデッドロックしたり、I/O待ちで止まったりしている可能性が考えられます。

https://software.fujitsu.com/jp/manual/manualfiles/m170006/b1ws1318/01z200/b1318-a-06-06.html

まず、デッドロックが発生しているかどうかを確認します。Java VMには、デッドロックを検出する機能が備わっていますので、それを利用します。

デッドロックが発生していない場合は、フルスレッドダンプを分析して、ハングアップやスローダウンの原因を特定します。

https://software.fujitsu.com/jp/manual/manualfiles/m170006/b1ws1318/01z200/b1318-a-06-06.html

Java VMには、フルスレッドダンプを出力する機能が備わっています。その機能の一部として、デッドロックを検出する機能が実装されています。

https://software.fujitsu.com/jp/manual/manualfiles/m170006/b1ws1318/01z200/b1318-a-06-06.html

⇧ ってな感じで、「デッドロック」を検出する機能は、「Java JVM」に標準で備わってるらしいですと。(あくまで、Javaソースコードを確認するものかと。データベースなどの「デッドロック」を検出する場合は他の対応が必要かと。)

 

で、「Java JVM」標準で利用できる「デッドロック」の検出できるものとして「jstack」ってものがありますと。

「フルスレッドダンプ」を出力する機能の一部として「デッドロック」を出力する機能が実装されているそうなのですが、「スレッドダンプ」とは?
software.fujitsu.com

スレッドダンプはJavaプロセスの各スレッドのスタックトレースです。このスタックトレースを解析することで、そのプロセスの動作や問題点の調査を確認することが可能です

https://software.fujitsu.com/jp/manual/manualfiles/M060013/B1WN5041/06Z200/trbs19/trbs0198.htm

⇧ 上記サイト様によりますと、「Javaプロセスの各スレッドのスタックトレース」ということで、「Javaのプロセス」を確認できるってことみたい。

 

「jstack」 については、
aoking.hatenablog.jp

思ったように性能が出ない時やデッドロックっぽい現象が発生した際はこのツールを使ってスレッドの状態を取得、つまりスレッドダンプを得て、そこから解決の糸口を探す。

jstack でスレッドダンプを取る - にょきにょきブログ

⇧ 上記サイト様の説明にありますように「性能」で問題が出た場合なんかにも使えそうですと。

 肝心の「jstack」ってどこにあるの?

qiita.com

⇧ 上記サイト様によりますと、 

JAVA_HOME/bin/jstack

⇧ ってな感じで、利用するJavaのbinディレクトリに存在するんだと。

自分の場合は、WindowsEclipse使ってるケースになるけど、以下におりました。

[Eclipseをインストールしたディレクトリ]\pleiades\java\[Javaのバージョン]\bin\jstack.exe

f:id:ts0818:20201216114449p:plain

 

実際に「jstack」使ってみた

マルチスレッドなプログラミングを実施した状態で、「jstack」してみました。

マルチスレッドなプログラミングは、

github.com

⇧ 上記サイト様の「035:スレッド・スレッドグループ」「036:排他」を参考にさせていただきました。

以下、3つのファイルを用意します。ファイルの配置先のディレクトリがバラバラになってますが、1つのディレクトリにまとめてしまっても問題ないかと。

f:id:ts0818:20201216115318p:plain

■Answer036.java

package java_one_hundred_nock.thirtysix;

import java.util.ArrayList;
import java.util.List;

public class Answer036 {

  private List<Object> members = new ArrayList<>();

  public List<Object> getMembers() {
    return this.members;
  }

  public void add(String member) {
    synchronized (members) {
      members.add(member);
    }
  }

  public void replace(Object oldMember, Object newMember) {
    synchronized (members) {
      if(members.contains(oldMember)) {
        members.remove(oldMember);
        members.add(newMember);
      }
    }
  }
}

■MyThread.java

package java_one_hundred_nock.thirtyfive.answer002;

import java.util.Random;

import java_one_hundred_nock.thirtysix.Answer036;

public class MyThread extends Thread {
  Answer036 answer;

  MyThread (ThreadGroup group, String name, Answer036 answer) {
    super(group, name);
    this.answer = answer;
  }

  public void run () {
    try {
      String newMemberStr = null;
      String oldMemberStr = null;

      Random rnd = new Random();
      sleep((rnd.nextInt(5)+1)*1000);
      newMemberStr = "No."+(rnd.nextInt(5)+1)*1000;
      oldMemberStr = "No."+(rnd.nextInt(5)+1)*1000;
      this.answer.add(newMemberStr);
      this.answer.replace(oldMemberStr, newMemberStr);

    } catch (InterruptedException e) {
      // TODO 自動生成された catch ブロック
      e.printStackTrace();
    }
  }
}

■Answer035.java

package java_one_hundred_nock.thirtyfive.answer002;

import java_one_hundred_nock.thirtysix.Answer036;

public class Answer035 {

  public static void main(String[] args) throws InterruptedException {
    // TODO 自動生成されたメソッド・スタブ
    ThreadGroup groupA = new ThreadGroup("groupA");
    ThreadGroup groupB = new ThreadGroup("groupB");
    Answer036 answer = new Answer036();

    for (int i = 0; i < 100; i++) {
      Thread threadA = new MyThread(groupA, "threadA", answer);
      Thread threadB = new MyThread(groupB, "threadB", answer);

      threadA.start();
      threadA.join();
      threadB.start();
      threadB.join();

    }

    for (int i = 0; i < 10; i++) {
      try {
        System.out.println("GroupA : " + groupA.activeCount());
        System.out.println("GroupB : " + groupB.activeCount());

        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // TODO 自動生成された catch ブロック
        e.printStackTrace();
      }
    }

    for (Object obj: answer.getMembers()) {
      System.out.println("結果" + obj.toString());
    }
  }
}

⇧ 上記のファイルを保存したらば、

「main」メソッドのある「Answer035.java」を選択して右クリックで「実行(R)」>「Java アプリケーション」を選択してプログラムを実行します。

f:id:ts0818:20201216120138p:plain

実行後、処理が進んでると思うので、「コマンドプロンプト」で以下を実施して処理中の「JavaのプロセスID」を取得で。

[利用したいJava]/bin/jps

f:id:ts0818:20201216120532p:plain

jstackの実行。

[利用したいJava]/bin/jstack [JavaのプロセスID]

f:id:ts0818:20201216120843p:plain

f:id:ts0818:20201216120924p:plain

⇧ と言うわけで、「デッドロック」は起きていなかった模様。

Eclipseのコンソールのほうも処理が終了してるのが確認できました。

なんか、Java.lang.Thread.joinってのが処理を遅延させてたっぽいです。

docs.oracle.com

public final void join()
                throws InterruptedException

このスレッドが終了するのを待機します。

このメソッド呼び出しの動作は、次の呼び出しの動作とまったく同一です。

join(0)

https://docs.oracle.com/javase/jp/7/api/java/lang/Thread.html#join()

⇧ ってあって、

引数0の意味はと言うと、 

public final void join(long millis)
                throws InterruptedException

このスレッドが終了するのを、最高で millis ミリ秒待機します。0 のタイムアウトは無期限に待機することを意味します。

https://docs.oracle.com/javase/jp/7/api/java/lang/Thread.html#join(long)

⇧ Oh, my gosh...

つまり、「スレッドA」「スレッドB」の2つがあったとしたら、「スレッドA」が終了するまで「スレッドB」は処理に入れないということで、時間がかかったってことですかね...

なので、ソースコードを修正しました。

■修正前

    for (int i = 0; i < 100; i++) {
      Thread threadA = new MyThread(groupA, "threadA", answer);
      Thread threadB = new MyThread(groupB, "threadB", answer);

      threadA.start();
      threadA.join();
      threadB.start();
      threadB.join();

    }

■修正後

    for (int i = 0; i < 100; i++) {
      new MyThread(groupA, "threadA", answer).start();
      new MyThread(groupB, "threadB", answer).start();
    }

まだ、気になることがありまして、Collection.contains の挙動ってこれで良いんでしたっけ?

engineer-club.jp

Collectionの中に持っているインスタンスと、containsで指定されたインスタンスが同じかはObject.equalsの結果で判定されます。ですので、equalsがしっかりとオーバーライドされていなければcontainsは動きません。

StringもListもMapもcontainsで! Javaで「含む」をチェックする方法

Javaの標準APIのクラスなら大抵は大丈夫ですが、自作のクラスを使う場合は、きちんとequalsをオーバーライドしているか確認しましょう。

StringもListもMapもcontainsで! Javaで「含む」をチェックする方法

⇧上記サイト様によりますと、Javaの標準のAPIのクラスなら大丈夫そうとのことですが、今回は、Listの要素のデータ型を「java.lang.Object」だから良くないのかな?、って思ったんだけど、

javadoc(ドキュメント)を確認してみた。

docs.oracle.com

Collections Frameworkインタフェース内の多数のメソッドは、equalsメソッドとの関連で定義されます。たとえば、contains(Object o)メソッドの仕様は、「このコレクションに (o==null ? e==null : o.equals(e))を満たす要素eが1つ以上含まれる場合にのみ、trueを返す」というものです。この仕様は、「null以外の引数oを使用してCollection.containsを呼び出すと、要素eo.equals(e)が呼び出される」と理解すべきではありません。実装は、最初に2つの要素のハッシュ・コードを比較するなど、equals呼出しを回避するための最適化を自由に実装できます。(Object.hashCode()仕様では、等価ではないハッシュ・コードを保持する2つのオブジェクトは等価ではないことが保証されます。)通常、さまざまなCollections Frameworkインタフェースの実装で、実装者が適切と判断するなら、基本となるObjectメソッドの指定された動作を自由に利用できます。

https://docs.oracle.com/javase/jp/8/docs/api/java/util/Collection.html

⇧ どうやら、「hashCode」が影響するってことなのかね?

まぁ、実際に、「Collection.contains」の実装を確認してみるかということで、まずは、「スーパー実装」を確認してみると、

public abstract class AbstractCollection<E> extends Object implements Collection<E>

   /**
     * {@inheritDoc}
     *
     * @implSpec
     * This implementation iterates over the elements in the collection,
     * checking each element in turn for equality with the specified element.
     *
     * @throws ClassCastException   {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }

⇧ ってな実装ですと。

ただ、今回は、「ArrayList」を使ってるので、以下になる感じですかね。

    /**
     * Returns {@code true} if this list contains the specified element.
     * More formally, returns {@code true} if and only if this list contains
     * at least one element {@code e} such that
     * {@code Objects.equals(o, e)}.
     *
     * @param o element whose presence in this list is to be tested
     * @return {@code true} if this list contains the specified element
     */
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

java.util.ArrayList<E>#indexOfindexOfを見てみると、

    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index {@code i} such that
     * {@code Objects.equals(o, get(i))},
     * or -1 if there is no such index.
     */
    public int indexOf(Object o) {
        return indexOfRange(o, 0, size);
    }

java.util.ArrayList<E>#indexOfRangeを見てみると、

   int indexOfRange(Object o, int start, int end) {
        Object[] es = elementData;
        if (o == null) {
            for (int i = start; i < end; i++) {
                if (es[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = start; i < end; i++) {
                if (o.equals(es[i])) {
                    return i;
                }
            }
        }
        return -1;
    }

最終的に、java.lang.Object#equalsを使ってるので、確認してみると、

    /**
     * Indicates whether some other object is "equal to" this one.
     * <p>
     * The {@code equals} method implements an equivalence relation
     * on non-null object references:
     * <ul>
     * <li>It is <i>reflexive</i>: for any non-null reference value
     *     {@code x}, {@code x.equals(x)} should return
     *     {@code true}.
     * <li>It is <i>symmetric</i>: for any non-null reference values
     *     {@code x} and {@code y}, {@code x.equals(y)}
     *     should return {@code true} if and only if
     *     {@code y.equals(x)} returns {@code true}.
     * <li>It is <i>transitive</i>: for any non-null reference values
     *     {@code x}, {@code y}, and {@code z}, if
     *     {@code x.equals(y)} returns {@code true} and
     *     {@code y.equals(z)} returns {@code true}, then
     *     {@code x.equals(z)} should return {@code true}.
     * <li>It is <i>consistent</i>: for any non-null reference values
     *     {@code x} and {@code y}, multiple invocations of
     *     {@code x.equals(y)} consistently return {@code true}
     *     or consistently return {@code false}, provided no
     *     information used in {@code equals} comparisons on the
     *     objects is modified.
     * <li>For any non-null reference value {@code x},
     *     {@code x.equals(null)} should return {@code false}.
     * </ul>
     * <p>
     * The {@code equals} method for class {@code Object} implements
     * the most discriminating possible equivalence relation on objects;
     * that is, for any non-null reference values {@code x} and
     * {@code y}, this method returns {@code true} if and only
     * if {@code x} and {@code y} refer to the same object
     * ({@code x == y} has the value {@code true}).
     * <p>
     * Note that it is generally necessary to override the {@code hashCode}
     * method whenever this method is overridden, so as to maintain the
     * general contract for the {@code hashCode} method, which states
     * that equal objects must have equal hash codes.
     *
     * @param   obj   the reference object with which to compare.
     * @return  {@code true} if this object is the same as the obj
     *          argument; {@code false} otherwise.
     * @see     #hashCode()
     * @see     java.util.HashMap
     */
    public boolean equals(Object obj) {
        return (this == obj);
    }

⇧ ってな感じで、javadocが異様に長いんだけど、見た感じ、Object.classのインスタンスで比較してますと。

じゃあ、Object.classのインスタンスってどうできるのか?って言うと、

    /**
     * Constructs a new object.
     */
    @HotSpotIntrinsicCandidate
    public Object() {}

⇧「jdk.internal.HotSpotIntrinsicCandidate」っていうアノテーションが何かしら影響してるとは思うんだけど、

/**
 * The {@code @HotSpotIntrinsicCandidate} annotation is specific to the
 * HotSpot Virtual Machine. It indicates that an annotated method
 * may be (but is not guaranteed to be) intrinsified by the HotSpot VM. A method
 * is intrinsified if the HotSpot VM replaces the annotated method with hand-written
 * assembly and/or hand-written compiler IR -- a compiler intrinsic -- to improve
 * performance. The {@code @HotSpotIntrinsicCandidate} annotation is internal to the
 * Java libraries and is therefore not supposed to have any relevance for application
 * code.
 *
 * Maintainers of the Java libraries must consider the following when
 * modifying methods annotated with {@code @HotSpotIntrinsicCandidate}.
 *
 * <ul>
 * <li>When modifying a method annotated with {@code @HotSpotIntrinsicCandidate},
 * the corresponding intrinsic code in the HotSpot VM implementation must be
 * updated to match the semantics of the annotated method.</li>
 * <li>For some annotated methods, the corresponding intrinsic may omit some low-level
 * checks that would be performed as a matter of course if the intrinsic is implemented
 * using Java bytecodes. This is because individual Java bytecodes implicitly check
 * for exceptions like {@code NullPointerException} and {@code ArrayStoreException}.
 * If such a method is replaced by an intrinsic coded in assembly language, any
 * checks performed as a matter of normal bytecode operation must be performed
 * before entry into the assembly code. These checks must be performed, as
 * appropriate, on all arguments to the intrinsic, and on other values (if any) obtained
 * by the intrinsic through those arguments. The checks may be deduced by inspecting
 * the non-intrinsic Java code for the method, and determining exactly which exceptions
 * may be thrown by the code, including undeclared implicit {@code RuntimeException}s.
 * Therefore, depending on the data accesses performed by the intrinsic,
 * the checks may include:
 *
 *  <ul>
 *  <li>null checks on references</li>
 *  <li>range checks on primitive values used as array indexes</li>
 *  <li>other validity checks on primitive values (e.g., for divide-by-zero conditions)</li>
 *  <li>store checks on reference values stored into arrays</li>
 *  <li>array length checks on arrays indexed from within the intrinsic</li>
 *  <li>reference casts (when formal parameters are {@code Object} or some other weak type)</li>
 *  </ul>
 *
 * </li>
 *
 * <li>Note that the receiver value ({@code this}) is passed as a extra argument
 * to all non-static methods. If a non-static method is an intrinsic, the receiver
 * value does not need a null check, but (as stated above) any values loaded by the
 * intrinsic from object fields must also be checked. As a matter of clarity, it is
 * better to make intrinisics be static methods, to make the dependency on {@code this}
 * clear. Also, it is better to explicitly load all required values from object
 * fields before entering the intrinsic code, and pass those values as explicit arguments.
 * First, this may be necessary for null checks (or other checks). Second, if the
 * intrinsic reloads the values from fields and operates on those without checks,
 * race conditions may be able to introduce unchecked invalid values into the intrinsic.
 * If the intrinsic needs to store a value back to an object field, that value should be
 * returned explicitly from the intrinsic; if there are multiple return values, coders
 * should consider buffering them in an array. Removing field access from intrinsics
 * not only clarifies the interface with between the JVM and JDK; it also helps decouple
 * the HotSpot and JDK implementations, since if JDK code before and after the intrinsic
 * manages all field accesses, then intrinsics can be coded to be agnostic of object
 * layouts.</li>
 *
 * Maintainers of the HotSpot VM must consider the following when modifying
 * intrinsics.
 *
 * <ul>
 * <li>When adding a new intrinsic, make sure that the corresponding method
 * in the Java libraries is annotated with {@code @HotSpotIntrinsicCandidate}
 * and that all possible call sequences that result in calling the intrinsic contain
 * the checks omitted by the intrinsic (if any).</li>
 * <li>When modifying an existing intrinsic, the Java libraries must be updated
 * to match the semantics of the intrinsic and to execute all checks omitted
 * by the intrinsic (if any).</li>
 * </ul>
 *
 * Persons not directly involved with maintaining the Java libraries or the
 * HotSpot VM can safely ignore the fact that a method is annotated with
 * {@code @HotSpotIntrinsicCandidate}.
 *
 * The HotSpot VM defines (internally) a list of intrinsics. Not all intrinsic
 * are available on all platforms supported by the HotSpot VM. Furthermore,
 * the availability of an intrinsic on a given platform depends on the
 * configuration of the HotSpot VM (e.g., the set of VM flags enabled).
 * Therefore, annotating a method with {@code @HotSpotIntrinsicCandidate} does
 * not guarantee that the marked method is intrinsified by the HotSpot VM.
 *
 * If the {@code CheckIntrinsics} VM flag is enabled, the HotSpot VM checks
 * (when loading a class) that (1) all methods of that class that are also on
 * the VM's list of intrinsics are annotated with {@code @HotSpotIntrinsicCandidate}
 * and that (2) for all methods of that class annotated with
 * {@code @HotSpotIntrinsicCandidate} there is an intrinsic in the list.
 *
 * @since 9
 */
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface HotSpotIntrinsicCandidate {
}

⇧ 何か、javadocがむちゃくちゃ長い...

それはさておき、「hashCode」メソッドについては、

    /**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, provided no information
     *     used in {@code equals} comparisons on the object is modified.
     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.
     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined
     * by class {@code Object} does return distinct integers for
     * distinct objects. (The hashCode may or may not be implemented
     * as some function of an object's memory address at some point
     * in time.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    @HotSpotIntrinsicCandidate
    public native int hashCode();

⇧ ってな感じで、「native」ってものが付いてるんだけど、

java-code.jp

native修飾子は、そのメソッドがネイティブメソッドである(Java以外の言語で実装されている)ことを示します。よって、native修飾子が指定された場合には、メソッド定義でも本体は省略し、中身は改めてCなどの言語で実装しなければなりません。

native修飾子 | Javaコード入門

⇧ 「native修飾子」って言うらしい。

気になったのは、「Object.class」で、

  • public Object() {}
  • public final native Class<?> getClass();
  • public native int hashCode();
  • protected native Object clone() throws CloneNotSupportedException;
  • public final native void notify()

に「@HotSpotIntrinsicCandidate」のアノテーションが付与されてるんだけど、どういった意味なのか分からん...

おまけに、上記の内、「コンストラクタ」以外は全て「native」なメソッドというね。

脱線しましたが、Listの要素にCollection.containsとかを実装するんであれば、Listの要素のデータ型については、注意が必要と思いきや、

stackoverflow.com

That doesn’t sound right: contains uses equals rather than ==, so if the string is in the list, it should be found. This can be verified in the indexOf method of the superclass AbstractList used by ArrayList.

https://stackoverflow.com/questions/12552048/java-contains-doesnt-work-as-expected-because-somestring-somestring

⇧ そもそもとして、ArrayListのcontainsメソッドは「==」で判定するんでなくて「equals」で判定するんだって言ってるんだけど、これはちょっと説明が足りなくて、確かに、ArrayListのcontainsメソッドは最終的には、「Object.equals」使ってたし、「Object.equals」は「==」で判定してるんだけど、containsメソッドの引数がObject型なので、例えば、String型の引数が渡された場合、String型で「equals」メソッドがOverrideされてるから「equals」で判定されるってことになるんですな。

なので、Listの要素に「Object型」を指定しても間違いではないとは思うんだけど、「Object型」にしてしまうと、どんなデータ型が代入されてるか分かり辛くなるし、Javaってせっかくタイプセーフな仕様のプログラミング言語を目指してたんじゃないかな?、って思ったので可能な限り「Object型」は使わないほうが良いのかなって思った次第です。(今回は、Listの要素を「Object型」にしちゃってるけど...)

外部からデータが渡ってくる場合は、「Object型」にせざるを得ないケースもあるかもだけど。(JSONデータとか?)

ちょっと紛らわしいのが、 

qiita.com

⇧「Objects.equals」ってのもあるって言うね。

あとは、  miya2000.hatenadiary.org

⇧ Listだけじゃなくて、Mapとかでも問題ありみたいですね... 

 

デッドロック以外の部分に脱線しまくりましたが、Javaってタイプセーフになるかどうかって、コーディング次第ってことなのかね...

相変わらず、Javaの仕様の闇が深いかな...

今回はこのへんで。