简体   繁体   English

为什么同步块比同步方法更好?

[英]Why is synchronized block better than synchronized method?

I have started learning synchronization in threading.我已经开始学习线程同步。

Synchronized method:同步方法:

public class Counter {

   private static int count = 0;

   public static synchronized int getCount() {
      return count;
   }

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

}

Synchronized block:同步块:

public class Singleton {

   private static volatile Singleton _instance;

   public static Singleton getInstance() {
      if (_instance == null) {
         synchronized(Singleton.class) {
            if (_instance == null)
               _instance = new Singleton();
         }
      }
      return _instance;
   }
}

When should I use synchronized method and synchronized block?什么时候应该使用synchronized方法和synchronized块?

Why is synchronized block better than synchronized method ?为什么synchronized块比synchronized方法更好?

It's not a matter of better, just different.这不是更好的问题,只是不同。

When you synchronize a method, you are effectively synchronizing to the object itself.当您同步一个方法时,您实际上是在同步到对象本身。 In the case of a static method, you're synchronizing to the class of the object.在静态方法的情况下,您正在同步到对象的类。 So the following two pieces of code execute the same way:所以下面两段代码的执行方式是一样的:

public synchronized int getCount() {
    // ...
}

This is just like you wrote this.这就像你写的一样。

public int getCount() {
    synchronized (this) {
        // ...
    }
}

If you want to control synchronization to a specific object, or you only want part of a method to be synchronized to the object, then specify a synchronized block.如果要控制与特定对象的同步,或者只想将方法的一部分同步到该对象,则指定一个synchronized块。 If you use the synchronized keyword on the method declaration, it will synchronize the whole method to the object or class.如果在方法声明中使用了synchronized关键字,它会将整个方法同步到对象或类。

Although not usually a concern, from a security perspective, it is better to use synchronized on a private object, rather than putting it on a method.虽然通常不是一个问题,但从安全角度来看,最好在私有对象上使用同步,而不是将它放在方法上。

Putting it on the method means you are using the lock of the object itself to provide thread safety.将它放在方法上意味着您正在使用对象本身的锁来提供线程安全。 With this kind of mechanism, it is possible for a malicious user of your code to also obtain the lock on your object, and hold it forever, effectively blocking other threads.通过这种机制,您代码的恶意用户也有可能获得您对象的锁,并永久持有它,从而有效地阻塞其他线程。 A non-malicious user can effectively do the same thing inadvertently.非恶意用户可以在不经意间有效地做同样的事情。

If you use the lock of a private data member, you can prevent this, since it is impossible for a malicious user to obtain the lock on your private object.如果您使用私有数据成员的锁,则可以防止这种情况发生,因为恶意用户不可能获得您私有对象的锁。

private final Object lockObject = new Object();

public void getCount() {
    synchronized( lockObject ) {
        ...
    }
}

This technique is mentioned in Bloch's Effective Java (2nd Ed), Item #70 Bloch 的 Effective Java(第 2 版)第 70 项中提到了这种技术

The difference is in which lock is being acquired:不同之处在于获取的是哪个锁:

  • synchronized method acquires a lock on the whole object.同步方法获取整个对象的锁。 This means no other thread can use any synchronized method in the whole object while the method is being run by one thread.这意味着当一个线程正在运行该方法时,没有其他线程可以在整个对象中使用任何同步方法。

  • synchronized blocks acquires a lock in the object between parentheses after the synchronized keyword. synchronized 块在synchronized 关键字之后的括号之间获取对象中的锁。 Meaning no other thread can acquire a lock on the locked object until the synchronized block exits.这意味着在同步块退出之前,没有其他线程可以获取锁定对象的锁定。

So if you want to lock the whole object, use a synchronized method.因此,如果您想锁定整个对象,请使用同步方法。 If you want to keep other parts of the object accessible to other threads, use synchronized block.如果您想让其他线程可以访问对象的其他部分,请使用同步块。

If you choose the locked object carefully, synchronized blocks will lead to less contention, because the whole object/class is not blocked.如果仔细选择锁定的对象,同步块将导致较少的争用,因为整个对象/类都没有被阻塞。

This applies similarly to static methods: a synchronized static method will acquire a lock in the whole class object, while a synchronized block inside a static method will acquire a lock in the object between parentheses.这同样适用于静态方法:同步静态方法将获取整个类对象中的锁,而静态方法内的同步块将获取括号之间对象中的锁。

Difference between synchronized block and synchronized method are following:同步块同步方法的区别如下:

  1. synchronized block reduce scope of lock, but synchronized method's scope of lock is whole method.同步块减少了锁的范围,同步方法的锁范围是整个方法。
  2. synchronized block has better performance as only the critical section is locked but synchronized method has poor performance than block.同步块具有更好的性能,因为只有临界区被锁定,同步方法的性能比块差。
  3. synchronized block provide granular control over lock but synchronized method lock either on current object represented by this or class level lock.同步块提供对锁的细粒度控制,同步方法锁定由 this 或类级锁表示的当前对象。
  4. synchronized block can throw NullPointerException but synchronized method doesn't throw.同步块可以抛出 NullPointerException同步方法不会抛出。
  5. synchronized block: synchronized(this){}同步块: synchronized(this){}

    synchronized method: public synchronized void fun(){}同步方法: public synchronized void fun(){}

Define 'better'.定义“更好”。 A synchronized block is only better because it allows you to:同步块只是更好,因为它允许您:

  1. Synchronize on a different object在不同的对象上同步
  2. Limit the scope of synchronization限制同步范围

Now your specific example is an example of the double-checked locking pattern which is suspect (in older Java versions it was broken, and it is easy to do it wrong).现在,您的具体示例是可疑的双重检查锁定模式的示例(在较旧的 Java 版本中,它已损坏,并且很容易出错)。

If your initialization is cheap, it might be better to initialize immediately with a final field, and not on the first request, it would also remove the need for synchronization.如果您的初始化成本较低,最好立即使用 final 字段进行初始化,而不是在第一次请求时进行初始化,这也将消除同步的需要。

synchronized should only be used when you want your class to be Thread safe.仅当您希望您的类是线程安全的时才应使用同步 In fact most of the classes should not use synchronized anyways.事实上,大多数类无论如何都不应该使用同步。 synchronized method would only provide a lock on this object and only for the duration of its execution.同步方法只会在此对象上提供锁定,并且仅在其执行期间提供锁定 if you really wanna to make your classes thread safe, you should consider making your variables volatile or synchronize the access.如果你真的想让你的类线程安全,你应该考虑让你的变量可变同步访问。

one of the issues of using synchronized method is that all of the members of the class would use the same lock which will make your program slower.使用同步方法的问题之一是该类的所有成员都将使用相同的,这会使您的程序变慢。 In your case synchronized method and block would execute no different.在您的情况下,同步方法和块将执行没有什么不同。 what I'd would recommend is to use a dedicated lock and use a synchronized block something like this.我会推荐的是使用专用并使用类似这样的同步块

public class AClass {
private int x;
private final Object lock = new Object();     //it must be final!

 public void setX() {
    synchronized(lock) {
        x++;
    }
 }
}

In your case both are equivalent!在你的情况下,两者都是等价的!

Synchronizing a static method is equivalent to a synchronized block on corresponding Class object.同步一个静态方法相当于在相应的 Class 对象上同步一个块。

In fact when you declare a synchronized static method lock is obtained on the monitor corresponding to the Class object.实际上当你声明一个synchronized静态方法时,是在Class对象对应的monitor上获得锁的。

public static synchronized int getCount() {
    // ...
}

is same as

public int getCount() {
    synchronized (ClassName.class) {
        // ...
    }
}

Because lock is expensive, when you are using synchronized block you lock only if _instance == null , and after _instance finally initialized you'll never lock.因为锁是昂贵的,当你使用同步块时,你只在_instance == null锁定,并且在_instance最终初始化之后你将永远不会锁定。 But when you synchronize on method you lock unconditionally, even after the _instance is initialized.但是当您同步方法时,您会无条件锁定,即使在_instance初始化之后也是如此。 This is the idea behind double-checked locking optimization pattern http://en.wikipedia.org/wiki/Double-checked_locking .这是双重检查锁定优化模式http://en.wikipedia.org/wiki/Double-checked_locking背后的想法。

It should not be considered as a question of best for usage, but it really depends on the use case or the scenario.不应将其视为最佳使用问题,但它确实取决于用例或场景。

Synchronized Methods同步方法

An entire method can be marked as synchronized resulting an implicit lock on the this reference (instance methods) or class (static methods).可以将整个方法标记为同步,从而导致对 this 引用(实例方法)或类(静态方法)的隐式锁定。 This is very convenient mechanism to achieve synchronization.这是实现同步的非常方便的机制。

Steps A thread access the synchronized method.步骤线程访问同步方法。 It implicitly acquires the lock and execute the code.它隐式地获取锁并执行代码。 If other thread want to access the above method, it has to wait.如果其他线程要访问上述方法,则必须等待。 The thread can't get the lock, will be blocked and has to wait till the lock is released.线程无法获得锁,将被阻塞,必须等到锁被释放。

Synchronized Blocks同步块

To acquire a lock on an object for a specific set of code block, synchronized blocks are the best fit.要为一组特定的代码块获取对象上的锁,同步块是最合适的。 As a block is sufficient, using a synchronized method will be a waste.由于一个块就足够了,使用同步方法将是一种浪费。

More specifically with Synchronized Block , it is possible to define the object reference on which are want to acquire a lock.更具体地说,使用 Synchronized Block ,可以定义要获取锁的对象引用。

One classic difference between Synchronized block and Synchronized method is that Synchronized method locks the entire object. Synchronized 块和 Synchronized 方法之间的一个经典区别是 Synchronized 方法锁定整个对象。 Synchronized block just locks the code within the block.同步块只是锁定块内的代码。

Synchronized method: Basically these 2 sync methods disable multithreading.同步方法:基本上这两种同步方法禁用多线程。 So one thread completes the method1() and the another thread waits for the Thread1 completion.所以一个线程完成method1(),另一个线程等待Thread1 完成。

class SyncExerciseWithSyncMethod {类 SyncExerciseWithSyncMethod {

public synchronized void method1() {
    try {
        System.out.println("In Method 1");
        Thread.sleep(5000);
    } catch (Exception e) {
        System.out.println("Catch of method 1");
    } finally {
        System.out.println("Finally of method 1");
    }

}

public synchronized void method2() {
    try {
        for (int i = 1; i < 10; i++) {
            System.out.println("Method 2 " + i);
            Thread.sleep(1000);
        }
    } catch (Exception e) {
        System.out.println("Catch of method 2");
    } finally {
        System.out.println("Finally of method 2");
    }
}

} }

Output输出

In Method 1在方法 1 中

Finally of method 1方法一的最后

Method 2 1方法 2 1

Method 2 2方法 2 2

Method 2 3方法 2 3

Method 2 4方法 2 4

Method 2 5方法 2 5

Method 2 6方法 2 6

Method 2 7方法 2 7

Method 2 8方法 2 8

Method 2 9方法 2 9

Finally of method 2方法二的最后


Synchronized block: Enables multiple threads to access the same object at same time [Enables multi-threading].同步块:允许多个线程同时访问同一个对象【启用多线程】。

class SyncExerciseWithSyncBlock {类 SyncExerciseWithSyncBlock {

public Object lock1 = new Object();
public Object lock2 = new Object();

public void method1() {
    synchronized (lock1) {
        try {
            System.out.println("In Method 1");
            Thread.sleep(5000);
        } catch (Exception e) {
            System.out.println("Catch of method 1");
        } finally {
            System.out.println("Finally of method 1");
        }
    }

}

public void method2() {

    synchronized (lock2) {
        try {
            for (int i = 1; i < 10; i++) {
                System.out.println("Method 2 " + i);
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            System.out.println("Catch of method 2");
        } finally {
            System.out.println("Finally of method 2");
        }
    }
}

} }

Output输出

In Method 1在方法 1 中

Method 2 1方法 2 1

Method 2 2方法 2 2

Method 2 3方法 2 3

Method 2 4方法 2 4

Method 2 5方法 2 5

Finally of method 1方法一的最后

Method 2 6方法 2 6

Method 2 7方法 2 7

Method 2 8方法 2 8

Method 2 9方法 2 9

Finally of method 2方法二的最后

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM