插入时ConcurrentHashMap出现线程安全问题

时间:2013-02-26 03:04:20

标签: java multithreading thread-safety

我正在开发一个项目,其中我在具有不同模式的不同数据库中有两个表。这意味着我有两个不同的连接参数,用于使用JDBC连接这两个表 -

我们假设下面是config.property文件 -

TABLES: table1 table2

#For Table1
table1.url: jdbc:mysql://localhost:3306/garden
table1.user: gardener
table1.password: shavel
table1.driver: jdbc-driver

#For Table2
table2.url: jdbc:mysql://otherhost:3306/forest
table2.user: forester
table2.password: axe
table2.driver: jdbc-driver

下面的方法将阅读上面的config.properties文件并为每个表创建一个ReadTableConnectionInfo对象。

  private static void readPropertyFile() throws IOException {

    prop.load(Read.class.getClassLoader().getResourceAsStream("config.properties"));

    tableNames = Arrays.asList(prop.getProperty("TABLES").split(" "));

    for (String arg : tableNames) {

        ReadTableConnectionInfo ci = new ReadTableConnectionInfo();

        String url = prop.getProperty(arg + ".url");
        String user = prop.getProperty(arg + ".user");
        String password = prop.getProperty(arg + ".password");
        String driver = prop.getProperty(arg + ".driver");

        ci.setUrl(url);
        ci.setUser(user);
        ci.setPassword(password);
        ci.setDriver(driver);

        tableList.put(arg, ci);

    }
}

以下是ReadTableConnectionInfo class,其中包含特定表格的所有table connection info

public class ReadTableConnectionInfo {

    public String url;
    public String user;
    public String password;
    public String driver;
    public String percentage;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }
}

现在我正在为指定数量的线程创建ExecutorService并将此tableList对象(我通过阅读config.property file创建)传递给ReadTask class的构造函数 -

// create thread pool with given size
ExecutorService service = Executors.newFixedThreadPool(10);

for (int i = 0; i < 10; i++) {
    service.submit(new ReadTask(tableList));
}

下面是我的ReadTask类,它实现了Runnable interface,其中每个线程在执行任何有意义的操作之前为每个表创建两个连接。

class ReadTask implements Runnable {

    private Connection[] dbConnection = null;
    private ConcurrentHashMap<ReadTableConnectionInfo, Connection> tableStatement = new ConcurrentHashMap<ReadTableConnectionInfo, Connection>();

    public ReadTask(LinkedHashMap<String, XMPReadTableConnectionInfo> tableList) {
        this.tableLists = tableList;
    }


    @Override
    public void run() {

        try {

            int j = 0;
            dbConnection = new Connection[tableList.size()];

            //loop around the map values and make the connection list
            for (ReadTableConnectionInfo ci : tableList.values()) {

                dbConnection[j] = getDBConnection(ci.getUrl(), ci.getUser(), ci.getPassword(), ci.getDriver());
                tableStatement.putIfAbsent(ci, dbConnection[j]);

                j++;
            }

            //do other meaningful thing here
           }
       }
    }

在我上面的try块中,我正在创建dbConnection array来存储两个不同的数据库连接。然后我有一个tableStatement作为ConcurrentHashMap,我在其中存储ReadTableConnectionInfo对象的dbConnection。例如,Table1对象将在tableStatement ConcurrentHashMap中具有Table1连接。

问题陈述: -

我正在尝试使用我在thread safety issues ConcurrentHashMap中插入的方式或其他任何线程安全问题来查看我的run方法中是否存在任何潜在的tableStatement

1 个答案:

答案 0 :(得分:0)

您的任务类不在线程之间共享(在当前示例中),因此在访问对象内部状态时没有并发问题。目前,您的任务的每个实例仅在执行程序分配给它的线程中访问:

service.submit(new ReadTask(tableList));

使用上面的代码行,您实例化ReadTask的实例并要求执行程序在单独的线程中运行它。如果您没有访问任何其他线程中的对象,那么您就没有并发问题。

在您的示例中,实际上,您不需要使用并发哈希映射(因为您没有为外部对象提供任何修改该集合的方法)。

如果要执行任何操作来修改代码,请考虑使ReadTableConnectionInfo对象不可变。您可以通过以下方式执行此操作:

  • 将每个类实例变量声明为final。
  • 提供一个接受每个实例变量的值的构造函数
  • 删除类setter方法
  • readPropertyFile方法中使用新构造函数。