在多线程Java程序中处理静态变量

时间:2018-08-30 00:35:59

标签: java multithreading static

下面的工厂类用于获取执行驱动程序

public class DriverFactory {
    //holds the device config
    public static Map<String, String> devConfig = new ConcurrentHashMap<>();

    //other lines of code follow
}

此配置从外部数据源加载到junit类中,如下所示:

@RunWith(ConcurrentTestRunner.class)
public class MyTestRunner{
    static final int THREAD_COUNT = 1;

    @ThreadCount(THREAD_COUNT) @Override @Test
    public void run(){
        // Devices class returns config for the device
        DriverFactory.devConfig = Devices.getConfig()
        //other lines of code to perform execution
    }
}

如果在执行过程中其他类中需要设备配置,则可以通过以下方式访问它:

public class MobileActions{
    public void swipe(){
        String pf = DriverFactory.devConfig.get("Platform");
        //other lines of code
    }
}

这种方法(具有devConfig为静态)工作正常,只有一个线程。现在,为了支持跨设备并行执行,如果将线程数更改为2,则devConfig将始终具有由第二个线程设置的值。

为了避免此问题,如果将devConfig设为非静态,则必须在所有其他类中(例如,在上述MobileActions类中)注入此变量。 有没有一种方法可以使该变量保持静态,但在多线程执行期间仍然可以工作(每个线程应处理自己的devConfig副本)。我们还尝试将此变量设置为ThreadLocal <>,但这也无济于事。

非常感谢您的帮助! 谢谢

3 个答案:

答案 0 :(得分:1)

将DriverFactory中的devConfig设为私有。提供它的获取器和设置器。 如果您需要特定于线程,请将其设为本地线程。

public class DriverFactory {

    // holds the device config
    private static final ThreadLocal<Map<String, String>> devConfig = ThreadLocal
        .withInitial(ConcurrentHashMap::new);

    public static String getDevConfig(String key) {
        return this.devConfig.get().get(key);
    }

    public static void setDevConfig(Map<String, String> config) {
        this.devConfig.get().putAll(config);
   }
}

答案 1 :(得分:0)

您可以将devConfig包装到ConcurrentHashMap中:

public class DriverFactory {
    //holds the device config
    private static final String CONFIG_KEY = "config_key";
    private static final ConcurrentHashMap<String, Map<String, String>> devConfig = new ConcurrentHashMap<>();

    public static Map<String, String> setDevConfig(Map<String, String> devConfig) {
        return DriverFactory.devConfig.putIfAbsent(CONFIG_KEY, devConfig);
    }

    public static Map<String, String> getDevConfig() {
        return DriverFactory.devConfig.get(CONFIG_KEY);
    }

    //other lines of code follow
}

@RunWith(ConcurrentTestRunner.class)
public class MyTestRunner{
    static final int THREAD_COUNT = 1;

    @ThreadCount(THREAD_COUNT) @Override @Test
    public void run(){
        // Devices class returns config for the device
        DriverFactory.setDevConfig(Devices.getConfig())
        //other lines of code to perform execution
    }
}

public class MobileActions{
    public void swipe(){
        String pf = DriverFactory.getDevConfig().get("Platform");
        //other lines of code
    }
}

但是,如果您想一次执行Devices.getConfig(),则应该尝试java.util.concurrent.ConcurrentHashMap#computeIfAbsent或自定义锁定。像这样:

    public static Map<String, String> getDevConfig() {
        return DriverFactory.devConfig
                .computeIfAbsent(CONFIG_KEY, s -> Devices.getConfig());
    }

答案 2 :(得分:0)

最后找到了使用此链接的方法:

https://dmitrykrivenko.blogspot.com/2016/07/threadlocal-and-inheritablethreadlocal.html

使用InheritableThreadLocal而不是ThreadLocal。这样,我现在也可以访问子线程中的数据。

public class DriverFactory {

private static final ThreadLocal<Map<String, String>> driverData = new InheritableThreadLocal<>();


public static String getDriverData(String key){
    return driverData.get().get(key);
}   

public static void setDriverData(Map<String, String> driver){
    driverData.set(driver);
}

}