Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/382.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么同步块比同步方法更好?_Java_Multithreading_Synchronization - Fatal编程技术网

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

Java 为什么同步块比同步方法更好?,java,multithreading,synchronization,Java,Multithreading,Synchronization,我已经开始学习线程同步 同步方法: public class Counter { private static int count = 0; public static synchronized int getCount() { return count; } public synchronized setCount(int count) { this.count = count; } } 同步块: public class Sin

我已经开始学习线程同步

同步方法:

public class Counter {

   private static int count = 0;

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

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

}
同步块:

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;
   }
}
何时应使用
synchronized
方法和
synchronized


为什么
synchronized
块比
synchronized
方法更好?

这不是更好的问题,只是不同而已。

同步方法时,实际上是在同步对象本身。对于静态方法,您正在同步到对象的类。因此,以下两段代码以相同的方式执行:

public synchronized int getCount() {
    // ...
}
这就像你写的一样

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

如果要控制与特定对象的同步,或者只希望方法的一部分与对象同步,请指定
synchronized
块。如果在方法声明中使用
synchronized
关键字,它会将整个方法同步到对象或类。

在您的情况下,两者是等效的

同步静态方法相当于相应类对象上的同步块

public static synchronized int getCount() {
    // ...
}
事实上,当您声明同步静态方法锁时,会在与类对象对应的监视器上获得

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

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

定义“更好”。同步块更好,因为它允许您:

  • 在不同对象上同步
  • 限制同步的范围
  • 现在,您的特定示例是一个可疑模式的示例(在较旧的Java版本中,它被破坏了,很容易出错)


    如果您的初始化成本较低,那么最好立即使用最后一个字段进行初始化,而不是在第一次请求时进行初始化,这样还可以消除同步的需要。

    仅当您希望您的类是线程安全的时才应使用synchronized。事实上,大多数类无论如何都不应该使用synchronizedsynchronized方法仅在此对象上提供锁,并且仅在其执行期间提供锁。如果你真的想让你的类是线程安全的,你应该考虑让你的变量<强>易失< /强>或<强>同步< /强> Access。< /P> 使用同步方法的一个问题是,类的所有成员都将使用相同的,这将使程序变慢。在您的情况下,同步方法和块将执行相同的操作。我建议使用一个专用的和一个同步块类似的东西

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

    因为锁是昂贵的,当您使用同步块时,您只在
    \u instance==null
    时锁定,并且在
    \u instance
    最终初始化之后,您将永远不会锁定。但是,当您在方法上同步时,即使在初始化
    \u实例之后,您也会无条件地锁定。这就是双重检查锁定优化模式背后的思想。

    虽然通常不关心安全问题,但从安全角度来看,最好在私有对象上使用synchronized,而不是将其放在方法上

    将它放在方法上意味着您正在使用对象本身的锁来提供线程安全性。通过这种机制,代码的恶意用户也有可能获得对象上的锁,并永久持有它,从而有效地阻止其他线程。非恶意用户可以在无意中有效地做同样的事情

    如果使用私有数据成员的锁,则可以防止这种情况,因为恶意用户不可能获得私有对象的锁

    private final Object lockObject = new Object();
    
    public void getCount() {
        synchronized( lockObject ) {
            ...
        }
    }
    

    Bloch的《有效Java》(第二版)第70项中提到了这种技术。不同之处在于获得锁的位置:

    • 同步方法获取整个对象的锁。这意味着当方法由一个线程运行时,其他线程不能在整个对象中使用任何同步方法

    • synchronized块在synchronized关键字后括号之间的对象中获取锁。这意味着在同步块退出之前,没有其他线程可以获取锁定对象的锁

    因此,如果要锁定整个对象,请使用同步方法。如果要使对象的其他部分可供其他线程访问,请使用同步块

    如果仔细选择锁定的对象,同步块将导致较少的争用,因为整个对象/类都没有被阻塞


    这同样适用于静态方法:同步静态方法将在整个类对象中获取锁,而静态方法中的同步块将在括号之间的对象中获取锁。

    不应将其视为最佳使用问题,但这实际上取决于用例或场景

    同步方法

    整个方法可以标记为synchronized,从而在此引用(实例方法)或类(静态方法)上产生隐式锁。这是实现同步的非常方便的机制

    步骤 线程访问同步方法。它隐式地获取锁并执行代码。 如果其他线程想要访问上述方法,它必须等待。线程无法获得锁,将被阻塞,必须等待锁释放

    同步块

    要为一组特定的代码块获取对象锁,同步块是最合适的。由于块已足够,使用同步方法将是一种浪费


    更具体地说,对于同步块,可以定义要在其上获取锁的对象引用。

    同步块与同步方法之间的区别
    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");
            }
        }
    }