Does .class property of an object always return th

2019-08-08 11:35发布

问题:

Am I synchronizing on the same object instance in the following code? Can I do it safely?

Class Sync {}

Class A implements Runnable {
  void run() {
    synchronized(Sync.class) {
      System.out.println("I am synchronized");
    }
  }
}

Class B implements Runnable {
  void run() {
    synchronized(Sync.class) {
      System.out.println("I too am synchronized");
    }
  }
}

回答1:

You have to make a distinction between Loading of a Class and Resolving a Class.

Class literals and the special method Class.forName(…) are different than the method ClassLoader.loadClass(…). While a ClassLoader might implement the latter in strange ways, e.g. by returning different instances on each call, the JVM will resolve each class exactly once per context and remember the result.

The important point is, if the resolution of a symbolic reference could be redirected, a static final variable would not help either. If you have two instances of the same Class in your JVM, two different version of their static final fields would exist as well.

There is a one-by-one mapping between a Class instance and the JVM per class data.


To cite a few official words:

JVMSpec § 5.3.2. Loading Using a User-defined Class Loader

… First, the Java Virtual Machine determines whether L has already been recorded as an initiating loader of a class or interface denoted by N. If so, this class or interface is C, and no class creation is necessary. …

JVMSpec § 5.3.5. Deriving a Class from a class File Representation

The following steps are used to derive a Class object for the nonarray class or interface C denoted by N using loader L from a purported representation in class file format.

(1) First, the Java Virtual Machine determines whether it has already recorded that L is an initiating loader of a class or interface denoted by N. If so, this creation attempt is invalid and loading throws a LinkageError.

(5) The Java Virtual Machine marks C as having L as its defining class loader and records that L is an initiating loader of C (§5.3.4).

The important point as pointed out by § 5.3.5 is that each ClassLoader can define at most one class per unique symbolic name. It might return different instances by delegating to different loaders but then they would be remembered as the defining loader. And the JVM will ask the defining loader when resolving references from a Class to another Class. Or skip asking it as § 5.3.2 states when a Class for a given name and loader already exists.



回答2:

According to JLS 8.4.3.6 synchronizing on Class.forName("name") and, hence, .class is the right thing to do and it will work - in the same class, at least. Moreover, all static synchronized methods are synchronized on .class. As it says in the JLS, the following are virtually the same:

class Test {
    int count;
    synchronized void bump() {
        count++;
    }
    static int classCount;
    static synchronized void classBump() {
        classCount++;
    }
}

class Test {
    int count;
    void bump() {
        synchronized (this) { count++; }
    }
    static int classCount;
    static void classBump() {
        try {
            synchronized (Class.forName("Test")) {
                classCount++;
            }
        } catch (ClassNotFoundException e) {}
    }
}

However, as JLS 12.2 states, it is not certain when .class is called by different classes, since this call depends on the class loader:

Well-behaved class loaders maintain these properties:

Given the same name, a good class loader should always return the same class object.

If a class loader L1 delegates loading of a class C to another loader L2, then for any type T that occurs as the direct superclass or a direct superinterface of C, or as the type of a field in C, or as the type of a formal parameter of a method or constructor in C, or as a return type of a method in C, L1 and L2 should return the same Class object.

A malicious class loader could violate these properties. However, it could not undermine the security of the type system, because the Java Virtual Machine guards against this.

So if you are using a standard Java class loader - you should be fine. If you are using some custom class loaders, however, you should watch out for errors.



回答3:

Only if they have the same classloader.

If you have a root-classloader and two child-class-loader the two child-class-loader (ie URLClassLoader) load the .class once per classloader, they will work async.

Load Sync in the root-classloader will be save.