Невозможно понять работу летучих полей и как они работают с несколькими потоками при совместном использовании значений - PullRequest
0 голосов
/ 10 октября 2018

Я работаю над программой, которая должна загружать одну и ту же информацию из файлов опций в несколько потоков.Поэтому я сделал простое представление класса каждого файла на основе абстрактной модели: (показан ниже 1 пример многих из этих классов)

package OptionManager;

import java.util.Properties;

public class EngineOptions extends AbstractOptions{
    //values
    private static String debugEnabled;
    private static String debugAvgLoadtime;
    private static String showShaderUsed;
    private static String mainLanguage;

    //keys
    public static final String DEBUGENABLED_KEY = "debugEnabled";
    public static final String DEBUGAVGLOADTIME_KEY = "debugAvgLoadtime";
    public static final String SHOWSHADERUSED_KEY = "showShaderUsed";
    public static final String MAINLANGUAGE_KEY = "mainLanguage";

    public static String getProperty(String key) {
        return properties.getProperty(key);
    }

    public static void loadFromFile(String filename) {      
        OptionReader loader = new OptionReader(filename);
        //load properties
        debugEnabled = loader.getProperty(DEBUGENABLED_KEY);
        debugAvgLoadtime = loader.getProperty(DEBUGAVGLOADTIME_KEY);
        showShaderUsed = loader.getProperty(SHOWSHADERUSED_KEY);
        mainLanguage = loader.getProperty(MAINLANGUAGE_KEY);

        properties.put(DEBUGENABLED_KEY, debugEnabled);
        properties.put(DEBUGAVGLOADTIME_KEY, debugAvgLoadtime);
        properties.put(SHOWSHADERUSED_KEY, showShaderUsed);
        properties.put(MAINLANGUAGE_KEY, mainLanguage);
    }
}

Вот абстрактный класс, который он использует:

package OptionManager;

import java.util.Properties;

public abstract class AbstractOptions {
    protected static volatile Properties properties;

    public static void setupProperties() {
        properties = new Properties();
    }

    public static void setupProperties(Properties properties) {}

    public static void setProperty(String key, String value) {
        if(properties.getProperty(key) == null) {
            //throw exception.
        }
        properties.setProperty(key, value);
    }

    public static String getProperty(String key) {
        System.out.println(properties);
        return properties.getProperty(key);
    }

    //public static void loadFromFile(String filename) {}
}

Я могу загрузить файлы опций в главном потоке (созданном JVM для запуска программы) и использовать эту систему в этом потоке, чтобы получить все нужные мне опции.Фактически, в качестве теста я печатаю весь список параметров в классе EngineOptions каждый раз, когда получаю доступ к параметру, который выдает следующий результат (при получении параметра mainLanguage):

{mainLanguage = language_IT, debugAvgLoadtime = 1, debugEnabled = 1, showShaderUsed = 1} language_IT

Однако, когда я пытаюсь получить доступ к той же опции внутри другого потока (созданного основным потоком и запущенного после того, как я напечатал вывод выше), я получаю это:

{} null

Это наводит меня на мысль, что для каждого потока значения моих статических полей не являются общими.Поэтому я нашел этот ответ , в котором предлагалось использовать «volatile» для решения проблемы.Однако это не сработало.

Другой вариант - загружать файлы опций в каждом потоке, но я бы не хотел этого делать, потому что причиной, по которой мне пришлось загружать файлы опций несколько раз для каждого потока, была причина, по которой я создалэта система в первую очередь.

Как я могу исправить эту проблему и поделиться значениями параметра для нескольких потоков?

РЕДАКТИРОВАТЬ:

Как создать новый параметрlist:

OptionHandler.addOptionFile(OptionHandler.ENGINE_OPTION_ID, new EngineOptions(), "EngineOptions");
OptionHandler.loadOptionListFromFile(OptionHandler.ENGINE_OPTION_ID, OptionHandler.ENGINE_OPTION_TYPE);

Как я вызываю опцию из потока:

String currentLang = OptionHandler.getProperty(EngineOptions.MAINLANGUAGE_KEY, OptionHandler.ENGINE_OPTION_ID);
        System.out.println(currentLang);

EDIT 2:

package OptionManager;

import java.util.HashMap;
import java.util.Map;

public class OptionHandler {
    private static HashMap<Integer, AbstractOptions> optionList;
    private static HashMap<Integer, String> optionFilename;

    //OptionFIleID's (Starting from 101 to 199)
    public static final int GRAPHIC_OPTION_ID = 101;
    public static final int ENGINE_OPTION_ID = 102;
    public static final int CURRENT_LANGUAGE_ID = 103;

    public static final int GRAPHIC_OPTION_TYPE = 201;
    public static final int ENGINE_OPTION_TYPE = 202;
    public static final int CURRENT_LANGUAGE_TYPE = 203;

    public static void setupOptions() {
        optionList = new HashMap<Integer, AbstractOptions>();
        optionFilename = new HashMap<Integer, String>();
    }

    public static void addOptionFile(int id, AbstractOptions options, String filename) {
        options.setupProperties();
        optionList.put(id, options);
        optionFilename.put(id, filename);
    }

    public static String getProperty(String optionKey, int optionFileID) {
        return optionList.get(optionFileID).getProperty(optionKey);
    }

    public static void loadOptionListFromFile(int id, int type) {
        System.out.println(optionFilename.get(id));
        if(type == GRAPHIC_OPTION_TYPE)
            GraphicOptions.loadFromFile(optionFilename.get(id));
        if(type == ENGINE_OPTION_TYPE)
            EngineOptions.loadFromFile(optionFilename.get(id));
        if(type == CURRENT_LANGUAGE_TYPE)
            CurrentLanguage.loadFromFile(optionFilename.get(id));
    }
}

1 Ответ

0 голосов
/ 11 октября 2018

Это проблема синхронизации потока. Если вы можете гарантировать, что действие параметров загрузки в главном потоке происходит до действия чтения в другом потоке, при использовании volatile проблем не возникает.Если вы не можете, это хорошая идея использовать API синхронизации потоков, такие как CountDownLatch.Это моя модификация вашего кода, Фокус на OptionHandler классе, и мой вывод правильный.

import java.util.Properties;

public abstract class AbstractOptions {
    protected volatile Properties properties;

    public void setupProperties() {
        properties = new Properties();
    }

    public void setupProperties(Properties properties) {}

    public void setProperty(String key, String value) {
        if(properties.getProperty(key) == null) {
            //throw exception.
        }
        properties.setProperty(key, value);
    }

    public String getProperty(String key) {
        System.out.println(properties);
        return properties.getProperty(key);
    }

    public abstract void loadFromFile(String filename);
}


public class EngineOptions extends AbstractOptions{
    //values
    private String debugEnabled;
    private String debugAvgLoadtime;
    private String showShaderUsed;
    private String mainLanguage;

    //keys
    public static final String DEBUGENABLED_KEY = "debugEnabled";
    public static final String DEBUGAVGLOADTIME_KEY = "debugAvgLoadtime";
    public static final String SHOWSHADERUSED_KEY = "showShaderUsed";
    public static final String MAINLANGUAGE_KEY = "mainLanguage";

    //public String getProperty(String key) {
    //return properties.getProperty(key);
    //}

    public void loadFromFile(String filename) {
        //OptionReader loader = new OptionReader(filename);
        //load properties
        debugEnabled = "language_IT";//loader.getProperty(DEBUGENABLED_KEY);
        debugAvgLoadtime = "1";//loader.getProperty(DEBUGAVGLOADTIME_KEY);
        showShaderUsed = "1";//loader.getProperty(SHOWSHADERUSED_KEY);
        mainLanguage = "1";//loader.getProperty(MAINLANGUAGE_KEY);

        properties.put(DEBUGENABLED_KEY, debugEnabled);
        properties.put(DEBUGAVGLOADTIME_KEY, debugAvgLoadtime);
        properties.put(SHOWSHADERUSED_KEY, showShaderUsed);
        properties.put(MAINLANGUAGE_KEY, mainLanguage);
    }
}

import java.util.HashMap;
import java.util.concurrent.CountDownLatch;

public class OptionHandler {
    private static HashMap<Integer, AbstractOptions> optionList;
    private static HashMap<Integer, String> optionFilename;
    private static CountDownLatch sync;

    static {
        setupOptions();
    }

    //OptionFIleID's (Starting from 101 to 199)
    public static final int GRAPHIC_OPTION_ID = 101;
    public static final int ENGINE_OPTION_ID = 102;
    public static final int CURRENT_LANGUAGE_ID = 103;

    public static final int GRAPHIC_OPTION_TYPE = 201;
    public static final int ENGINE_OPTION_TYPE = 202;
    public static final int CURRENT_LANGUAGE_TYPE = 203;

    public static void setupOptions() {
        optionList = new HashMap<Integer, AbstractOptions>();
        optionFilename = new HashMap<Integer, String>();
        //initialize
        sync = new CountDownLatch(1);
    }

    public static void addOptionFile(int id, AbstractOptions options, String filename) {
        options.setupProperties();
        optionList.put(id, options);
        optionFilename.put(id, filename);
    }

    public static String getProperty(String optionKey, int optionFileID) {
        try {
            //await when the property is not ready yet
            sync.await();
        } catch (InterruptedException e) {
            //log("thread was interrupted")
            Thread.currentThread().interrupt();
        }
        return optionList.get(optionFileID).getProperty(optionKey);
    }

    public static void loadOptionListFromFile(int id, int type) {
        System.out.println(optionFilename.get(id));
//        if(type == GRAPHIC_OPTION_TYPE)
//            GraphicOptions.loadFromFile(optionFilename.get(id));
        if(type == ENGINE_OPTION_TYPE)
            optionList.get(id).loadFromFile(optionFilename.get(id));
//        if(type == CURRENT_LANGUAGE_TYPE)
//            CurrentLanguage.loadFromFile(optionFilename.get(id));
        //Notify other threads that the property is ready
        sync.countDown();
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            String currentLang = 
                  OptionHandler.getProperty(EngineOptions.MAINLANGUAGE_KEY, 
                  OptionHandler.ENGINE_OPTION_ID);
            System.out.println(currentLang);
        }).start();
        Thread.sleep(3000);
        OptionHandler.addOptionFile(OptionHandler.ENGINE_OPTION_ID, new EngineOptions(), "EngineOptions");
        OptionHandler.loadOptionListFromFile(OptionHandler.ENGINE_OPTION_ID, OptionHandler.ENGINE_OPTION_TYPE);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...