疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

怎样在静态方法中使用 synchronized


 

   

  synchronized 的实现方式

  synchronized 的类型可以分为两种:

  synchronized method

  synchronized block

  两者的实现方式是不一样的,jvm 规范中写道,编译后的 synchronized method 会有一个 ACC_SYNCHRONIZED 的 flag,也就是说当 jvm 的方法调用指令(the method invocation instruction)从 the run-time constant pool 中查找到这个 method 的时候,已经知道它是一个synchronized method,所以锁操作是由方法调用以及返回指令来控制的。而 synchronized block 的锁是由 monitorenter 和 monitorexit 这两个指令来控制。

  可以通过 javap 命令来“反汇编”一下 class 文件。

  public class StaticMethodTest {

  public static synchronized void staticMethod() { }

  public static void staticMethod1() {

  synchronized (StaticMethodTest.class) {

  // ...

  }

  }

  public void memberMethod() { }

  }

  编译成 class 文件后执行

  javap -verbose StaticMethodTest

  1

  两个静态方法的输出结果分别为

  public static synchronized void staticMethod();

  descriptor: ()V

  flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED

  Code:

  stack=0, locals=0, args_size=0

  0: return

  LineNumberTable:

  line 2: 0

  public static void staticMethod1();

  descriptor: ()V

  flags: ACC_PUBLIC, ACC_STATIC

  Code:

  stack=2, locals=2, args_size=0

  0: ldc #2 // class StaticMethodTest

  2: dup

  3: astore_0

  4: monitorenter

  5: aload_0

  6: monitorexit

  7: goto 15

  10: astore_1

  11: aload_0

  12: monitorexit

  13: aload_1

  14: athrow

  15: return

  Exception table:

  from to target type

  5 7 10 any

  10 13 10 any

  其实不管静态方法还是成员方法,synchronized 的实现方式都是一样的,那么类和对象究竟有什么关系呢?

  类和对象

  首先要了解的就是类究竟是怎么来的。

  JVM 拿到编译器编译好的 class 文件后,首先会把文件载入到内存中,class 文件当然会有自己的格式,所以需要由 ClassLoader 来解析文件的内容,这个解析出来的内容会用一个 Class 类的实例 - Class object 来表示,这个 object 可以通过 Java 的 ClassName.class来获取。

  也就是说,Class object 是一个 Class 类型的实例(instance),而对象是一个 ClassName 的 instance。Class 和 ClassName都是类型,ClassName是由 class 关键字定义的,而Class是内置类型。

  因此成员方法的synchronized method 就等价于 synchronized (this) block,即下面两种方式是等价的。

  public synchronized void fun1() {

  // do something here

  }

  public synchronized void fun2() {

  synchronized (this) {

  // do something here

  }

  }

  成员方法是属于 this,而静态方法是属于 Class Object,那么静态方法的 synchronized method 也就等价于下面这种形式的 synchronized block 了。

  public static synchronized void fun2() {

  synchronized (ClassName.class) {

  // do something here

  }

  }

  验证代码

  定义了三个静态方法,分别采用不同的锁机制,并且每个方法都是一个死循环。然后再定义三个线程分别执行调用三个方法。

  import java.util.concurrent.*;

  public class SynchronizedTest {

  private static Object lock = new Object();

  public synchronized static void fun() {

  while (true) {

  System.out.println("in fun");

  try {

  TimeUnit.MILLISECONDS.sleep(500);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  }

  }

  public static void fun1() {

  synchronized (SynchronizedTest.class) {

  while (true) {

  System.out.println("in fun1");

  try {

  TimeUnit.MILLISECONDS.sleep(500);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  }

  }

  }

  public static void fun2() {

  synchronized (lock) {

  while (true) {

  System.out.println("in fun2");

  try {

  TimeUnit.MILLISECONDS.sleep(500);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  }

  }

  }

  public static void main(String[] args) {

  new Thread() {

  @Override

  public void run() {

  fun();

  }

  }.start();

  new Thread() {

  @Override

  public void run() {

  fun1();

  }

  }.start();

  new Thread() {

  @Override

  public void run() {

  fun2();

  }

  }.start();

  }

  }

  运行结果就是 in fun 和 in fun2 交替出现,就是没有 in fun1。

  只要运行 fun 的线程不交出锁,fun1 就无法被调用,因为他们都是共享了 Class object 的锁。