5

次のように定義されたネイティブ メソッド メソッドがあります。

public static native int doSomething();

ただし、このメソッドはスレッドセーフではありません。synchronizedというわけで、キーワードを入れてみましたが、今はこんな感じです。

public static synchronized native int doSomething();

これで問題が解決したように見えますが、実際に問題が解決するかどうかはわかりません。これは有効ですか?メソッドへのアクセスを実際に適切にロックしますか?

4

2 に答える 2

5

関連するJLS セクションを読んだ後、JLSには、メソッド定義に含まれることstaticを禁止するものは何もありません。関連する JVM 仕様nativeによると:

メソッドレベルの同期は、メソッドの呼び出しと戻りの一部として暗黙的に実行されます (§2.11.8)。同期されたメソッドは、ランタイム定数プールの method_info 構造体 (§4.6) で、メソッド呼び出し命令によってチェックされる ACC_SYNCHRONIZED フラグによって区別されます。ACC_SYNCHRONIZED が設定されているメソッドを呼び出すと、実行中のスレッドはモニターに入り、メソッド自体を呼び出し、メソッド呼び出しが正常に完了するか突然完了するかに関係なく、モニターを終了します。実行中のスレッドがモニターを所有している間、他のスレッドはモニターに入ることができません。同期メソッドの呼び出し中に例外がスローされ、同期メソッドが例外を処理しない場合、同期メソッドから例外が再スローされる前に、メソッドのモニターが自動的に終了します。

このため、生成されるバイトコードには、ブロックのようにmonitorenterormonitorexit命令はありません。synchronizedこの場合に生成されるのはinvokestatic、静的メソッドを呼び出すために だけです。static native synchronizedこの命令は、メソッド、static nativeメソッド、またはメソッドを呼び出すと生成されますstatic

生成されたバイトコードを使用したサンプル コードを次に示します。

    public static void main( String[] args ){
            doSomething1();
            System.out.println("Now do 2");
            doSomething2();
            System.out.println("native java");
            doSomethingJava();

            String s = "test";
            synchronized ( s ){
                    int x = 9 + 5;
            }
    }

    public static native void doSomething1();
    public static synchronized native void doSomething2();

    public static synchronized void doSomethingJava(){
            System.out.println("synchronized");
    }

生成されたバイトコード:

Compiled from "test.java"
class test {
  test();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #2                  // Method doSomething1:()V
       3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       6: ldc           #4                  // String Now do 2
       8: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      11: invokestatic  #6                  // Method doSomething2:()V
      14: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      17: ldc           #7                  // String native java
      19: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      22: invokestatic  #8                  // Method doSomethingJava:()V
      25: ldc           #9                  // String test
      27: astore_1      
      28: aload_1       
      29: dup           
      30: astore_2      
      31: monitorenter  
      32: bipush        14
      34: istore_3      
      35: aload_2       
      36: monitorexit   
      37: goto          47
      40: astore        4
      42: aload_2       
      43: monitorexit   
      44: aload         4
      46: athrow        
      47: return        
    Exception table:
       from    to  target type
          32    37    40   any
          40    44    40   any

  public static native void doSomething1();

  public static synchronized native void doSomething2();

  public static synchronized void doSomethingJava();
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #10                 // String synchronized
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return        
}
于 2013-08-16T18:04:11.187 に答える
2

まあ、それはメソッドへのアクセスを相互に排他的にします。それが適切にスレッドセーフであるというあなたの考えであるなら、私はそれが正しいと信じています。

[編集:] もちろん、そのメソッドがスレッドセーフかどうかをそのシグネチャだけから判断する方法はありません。実際、メソッドのソースでさえ十分な情報ではない場合があります。スレッド セーフとは、リソースへのアクセスを同期することです。メソッドがリソースにアクセスしない場合は、「synchronized」キーワードがなくてもスレッドセーフです。

synchronized キーワードは、リソース アクセスを同期する場合、つまり、クラスにプライベート フィールドがあり、それを変更するすべてのメソッドが同期される場合、何かをスレッド セーフにします。

于 2013-05-31T01:06:49.247 に答える