1

我不清楚的概念

Java线程进入实例同步java方法时获取对象级锁,进入静态同步java方法时获取类级锁。

当它说对象级锁和类级锁是什么意思?

例如:

 public class Counter{
      private static int count = 0;
      private int count2 = 0;

      public static synchronized int getCount(){
           return count;
      }
      public synchronized setCount(int count2){
           this.count2 = count2;
      }
 } 

此处的 getCount() 将锁定 Counter.class 对象,而 setCount() 将锁定当前对象(this)。是什么意思?这是否意味着当 getCount() 被调用时,另一个线程由于整个类被锁定而无法访问 setCount() ?

4

4 回答 4

1

static成员与类相关联,而不是与特定实例相关联。结果,static成员上的同步有效地同步整个类,因为静态是类成员

实例成员与类的实例相关联(例如,内在this锁),因此如果您在成员字段(非静态)上同步,this那么您就有一个与实例对象相关联的锁。

在您的示例getCount中,同步类的 intrinsik 锁。实际上你正在做:

public setCount(int count){
    synchronized(this){
        this.count = count;
    }  
}

如果您这样做,您将在班级级别同步:

static Object count = new Object();  
synchronized(count){  

}
于 2012-08-21T22:40:58.737 回答
1

Java 中的每个对象都有一个互斥体。由于 aclass由 type 的对象表示java.lang.Class,因此每个类也有一个互斥体。 synchronized实例方法锁定实例的互斥锁,而synchronized static方法锁定对应java.lang.Class实例的互斥锁。

class C {
  synchronized T myMethod() { ... }

  static synchronized T myStaticMethod() { ... }
}

相当于

class C {
  T myMethod() {
    synchronized (this) { ... }  // An object level lock.
  }

  static T myStaticMethod() {
    synchronized (C.class) { ... }  // A class level lock
    // is really just an object level lock an a Class.
  }
}

一旦您知道要在哪个对象上进行同步,您就可以synchronized通过分解关键字来理解它。

synchronized (x) {
  body();
}

表现得像

monitorenter(x);  // locks x or throws NullPointerException if x is null.
try {
  body();
} finally {
  monitorexit(x);  // unlocks x
}

其中monitorentermonitorexitJava 字节码指令,它们分别阻塞直到它们获得互斥体,然后释放互斥体。

由于synchronized引入了“受保护区域”,例如try ... finally,即使抛出异常,锁也会被释放,但杀死线程会使锁未释放,可能导致死锁

于 2012-08-21T22:45:39.657 回答
1

在 Java 中,每个类和一个类的每个实例都被赋予了它们的内在锁。这些是在进入和退出synchronized方法时获取和释放的锁。

像往常一样this是指setCount()被调用的实例,并且因为每个实例都有自己的内在锁,setCount()所以只要在您的类的另一个实例上调用它,第二次调用就不会阻塞,而是尝试调用setCount()另一个实例setCount()通话进行中会阻塞。

类锁和实例锁是不同的,因此getCount()永远setCount()不会互相妨碍。

于 2012-08-21T22:37:51.240 回答
1

当它说对象级锁和类级锁是什么意思?

当您锁定一个static方法时,您将锁定Class对象本身,并且每个ClassLoader. 在你的例子中,

public static synchronized int getCount(){

这是锁定Counter.class对象,与以下内容相同:

public static int getCount() {
    synchronized (Counter.class) {
    }

如果您锁定的是非方法,那么 static您锁定的是拥有该方法的对象的实例。在您的示例中:

public synchronized void setCount(int count){

这与锁定特定Counter实例相同,等效于:

public void setCount(int count){
    synchronized (this) {
       ...

因此,如果您有 2 个Counter对象,counter1并且counter2, 和 1 个线程正在调用counter1.getCount(),而另一个正在counter2.getCount()同时调用,那么它们都将锁定同一个Class对象,一个将阻塞另一个。

但是,如果 2 个线程改为调用counter1.setCount(...)counter2.setCount()它们将分别锁定不同的counter1对象counter2。他们不会互相阻挡。

如前所述,在您的 setter 和 getter 上具有不对称性是非常糟糕的形式,并且两者中的任何一个都是不寻常的static

这是否意味着当 getCount() 被调用时,另一个线程由于整个类被锁定而无法访问 setCount() ?

不。如果getCount()被调用,Counter.class则被锁定,而何时setCount(...)被调用counter1或被counter2锁定。唯一一次锁阻塞线程是当同一个对象被另一个线程锁定时。仅仅因为有一个锁Counter.class并不意味着有某种超级锁。唯一会阻塞另一个线程的时间是它太锁定了Counter.class

我会花点时间阅读 Sun关于如何synchronized工作的精彩文档。

于 2012-08-22T14:36:27.583 回答