Java 在静态变量中使用synchronized

Java 在静态变量中使用synchronized,java,multithreading,static-variables,Java,Multithreading,Static Variables,我有一张静态地图 private static Map<String, Car> cars = new HashMap<~>() //Map holding car objects 所以,如果我想把车锁上,为了螺纹安全,可以像下面这样做吗 private static void xyz() { synchronized(cars) { Car c = getCar("abc"); c.setColor("Green");

我有一张静态地图

private static Map<String, Car> cars = new HashMap<~>() //Map holding car objects
所以,如果我想把车锁上,为了螺纹安全,可以像下面这样做吗

private static void xyz() {
    synchronized(cars) {
        Car c = getCar("abc");
        c.setColor("Green");
    }
}
有什么建议吗?

Java的synchronized关键字可以应用于任何对象,以获取该对象上的锁或在继续之前同步该对象。如果任何其他进程尝试同时在同一对象上运行同步代码,它将阻塞,直到锁定该对象的进程退出同步块

同步对象不必在同步块的任何部分中实际使用;它可以是一个简单的互斥对象,其唯一用途是在同步块中锁定和解锁。但是,重要的是要注意,如果其他线程没有在对象上同步,那么在对象上同步并不能阻止它们修改对象。它是一个程序员/约定强制锁,而不是内置锁,并且使用共享对象的所有代码都必须同意在其上同步

例如,使用上面编写的代码,即使您的xyz方法在汽车上同步,您也可以编写另一种方法,如下所示:

public void changeCar() {
    Car myCar = cars.get("abc");
    myCar.setColor("Blue");
}
在不调用synchronized的情况下修改汽车。此方法可能在方法xyz修改abc car的同时修改abc car,即违反线程安全性,因为它不包含任何调用car上同步的代码

如果您想确保您的汽车地图是线程安全的,即永远不会同时被两种方法修改,您必须

确保任何修改cars的代码首先调用synchronizedcars 使用,这可以保证对其执行的put和get操作是线程安全的。 Java的synchronized关键字可以应用于任何对象,以获取该对象上的锁或在继续之前同步该对象。如果任何其他进程尝试同时在同一对象上运行同步代码,它将阻塞,直到锁定该对象的进程退出同步块

同步对象不必在同步块的任何部分中实际使用;它可以是一个简单的互斥对象,其唯一用途是在同步块中锁定和解锁。但是,重要的是要注意,如果其他线程没有在对象上同步,那么在对象上同步并不能阻止它们修改对象。它是一个程序员/约定强制锁,而不是内置锁,并且使用共享对象的所有代码都必须同意在其上同步

例如,使用上面编写的代码,即使您的xyz方法在汽车上同步,您也可以编写另一种方法,如下所示:

public void changeCar() {
    Car myCar = cars.get("abc");
    myCar.setColor("Blue");
}
在不调用synchronized的情况下修改汽车。此方法可能在方法xyz修改abc car的同时修改abc car,即违反线程安全性,因为它不包含任何调用car上同步的代码

如果您想确保您的汽车地图是线程安全的,即永远不会同时被两种方法修改,您必须

确保任何修改cars的代码首先调用synchronizedcars 使用,这可以保证对其执行的put和get操作是线程安全的。 这很好,但请注意,使用当前代码,您并没有完全锁定cars对象,您仍然可以使用其他方法访问cars并设置属性,除非您在cars对象上再次同步

但是,如果方法xyz是您设置属性的唯一位置,那么您的代码是线程安全的。

这很好,但请注意,使用当前代码,您没有完全锁定cars对象,您仍然可以使用其他方法访问cars并设置属性,除非您再次在cars对象上同步


但是,如果方法xyz是您设置属性的唯一位置,那么您的代码是线程安全的。

如果您只担心单个方法的同步,请使用

Map<String, Car> synchronizedMap = Collections.synchronizedMap(map);
这将自动向映射中的所有方法添加同步,并使每个方法调用原子化。这是您可能应该向线程公开的映射

但是,如果您需要跨多个方法调用进行同步,例如如果某个对象为null,则get后跟put,则需要使用上述方法


请注意,Car对象也需要是线程安全的,在没有看到该方法的情况下,我无法确定它是否是线程安全的。

如果您只担心单个方法的同步,请使用

Map<String, Car> synchronizedMap = Collections.synchronizedMap(map);
这将自动向映射中的所有方法添加同步,并使每个方法调用原子化。这是您可能应该向线程公开的映射

但是,如果您需要跨多个方法调用进行同步,例如如果某个对象为null,则get后跟put,则需要使用上述方法

请注意,Car对象也需要是线程安全的,在没有看到该方法的情况下,我无法确定它是否是线程安全的


不明白为什么这样做不好,您可以在return语句周围添加一个try/catch块来捕获NullPointerException并返回一个非null的结果。@JonLin我很困惑,因为,静态变量cars不能在同步块内直接访问。@RusselShingleton为什么要捕获NullPointerException而不是检查nulls?只使用if并首先避免NPE是非常有效的。@RussellShingleton我肯定会这么做,但我更关心同步的car变量,以及它是否会被锁定?我不明白为什么这样做不好,您可以在return语句周围添加一个try/catch块来捕获NullPointerException并返回一个非null的结果。@JonLin我很困惑,因为,静态变量cars不能在同步块内直接访问。@RusselShingleton为什么要捕获NullPointerException而不是检查nulls?只使用if并首先避免NPE是非常有效的。@RussellShingleton我肯定会这样做,但我更关心同步的car变量以及它是否会被锁定?但是我对这一行感到困惑,重要的是要注意,如果其他线程没有在对象上同步,那么简单地在对象上同步并不会阻止它们修改该对象。这意味着在方法中调用synchronizedcars不会神奇地阻止任何其他方法修改cars。它确实阻止了对xyz方法的多个调用并发执行,但它不会阻止像我编写的changeCar方法这样的方法与xyz并发执行,重要的是要注意,如果其他线程没有在对象上同步,那么简单地在对象上同步并不会阻止它们修改该对象。这意味着在方法中调用synchronizedcars不会神奇地阻止任何其他方法修改cars。它确实阻止了对xyz方法的多个调用并发执行,但它不会阻止像我编写的changeCar方法这样的方法与xyz并发执行。