5

我有一个这样定义的本机方法方法:

public static native int doSomething();

但是,此方法不是线程安全的。所以,我在上面放了一个synchronized关键字,所以现在看起来像这样:

public static synchronized native int doSomething();

这似乎解决了这个问题,但我不确定它是否真的解决了。这是有效的吗?它实际上是否正确锁定了对该方法的访问?

4

2 回答 2

5

在阅读了相关的JLS 部分之后,JLS 中没有任何内容禁止staticnative出现在方法定义中。根据相关的JVM规范:

方法级同步是隐式执行的,作为方法调用和返回的一部分(第 2.11.8 节)。同步方法在运行时常量池的 method_info 结构(第 4.6 节)中由 ACC_SYNCHRONIZED 标志来区分,该标志由方法调用指令检查。当调用设置了 ACC_SYNCHRONIZED 的方法时,执行线程进入监视器,调用方法本身,然后退出监视器,无论方法调用正常完成还是突然完成。在执行线程拥有监视器期间,没有其他线程可以进入它。如果在调用同步方法期间抛出异常,并且同步方法没有处理异常,则在将异常重新抛出同步方法之前自动退出该方法的监视器。

因此,生成的字节码没有任何指令monitorentermonitorexit指令,就像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 关键字使某些东西成为线程安全的,即您的类有一个私有字段,并且修改它的每个方法都是同步的。

于 2013-05-31T01:06:49.247 回答