1

我有一个看起来像这样的 HashMap -

 HashMap<String, TableConnectionInfo> tableList

这意味着它的值是一个TableConnectionInfo看起来像这样的类 -

public class TableConnectionInfo {

    public String url;
    public String user;
    public String password;
    public String driver;
    public String suffix;
    public String sql;

    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;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

}

在主线程中,我通过像这样从属性文件中读取上面的地图来填充上面的地图,之后这个地图将不会被修改。

    for (String arg : databaseNames) {

        TableConnectionInfo ci = new TableConnectionInfo();

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

        ci.setUrl(url);
        ci.setDriver(driver);
        ci.setPassword(password);
        ci.setSql(sql);
        ci.setSuffix(suffix);
        ci.setUser(user);
        tableList.put(arg, ci);
    }

现在我将此tableList映射传递给这样的各种线程,并且它不会被任何线程修改(通过进行集合调用)。每个线程都将使用 get 方法来获取所需的方法。

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

下面是我的包含可运行接口的任务类

class Task implements Runnable {

    private Connection[] dbConnection = null;
    private CallableStatement[] callableStatement = null;
    private ArrayList<Method> methods[] = null;

    private final HashMap<String, TableConnectionInfo> tableLists;

    public Task(HashMap<String, TableConnectionInfo> tableList) {
        this.tableLists = tableList;
    }

    @Override
    public void run() {

        try {

            int j = 0;
            dbConnection = new Connection[tableLists.size()];
            callableStatement = new CallableStatement[tableLists.size()];
            methods = new ArrayList[tableLists.size()];

            for (TableConnectionInfo ci : tableLists.values()) {

                dbConnection[j] = getDBConnection(ci.getUrl(), ci.getUser(), ci.getPassword(),  ci.getDriver());
                callableStatement[j] = dbConnection[j].prepareCall(ci.getSql());

                methods[j] = getRequiredMethods(ci.getSuffix());
                j++;
            }

          }
             }
       }

问题:-

现在我的问题是 - 在我的运行方法中,我调用了TableConnectionInfo类的 get 方法,所以它是否是线程安全的?由于多个线程将尝试进行 get 调用。所以我不确定我是否需要在这里做任何额外的事情?还是代码看起来不错?

4

5 回答 5

4

Java 中的参考值(以及原语)赋值是原子的。您不必担心参考值处于某种半分配状态,就像在较低级别的语言中那样。

话虽如此,是的,在您的类中调用 getter 是线程安全的,因为它们只是访问String不可变的对象,但是......如果其他线程通过 setter 更改了一个对象,您可能由于缓存而无法获得当前值。您想在as中声明所有String变量。这可以防止线程缓存旧值,从而防止 getter 将其返回给您。TableConnectionInfovolatile

如果您TableConnectionInfo包含 getter 正在访问的更复杂的对象(例如Lists 或Maps),而其他线程可能正在更改……那是另一回事。对可变的复杂数据结构的并发修改是不同的,需要同步。

其中...指向您的tableList. 如果可能被另一个线程(添加/删除)修改,那么你就有问题了。

如果您所做的只是读取(没有机会另一个线程正在写入/修改),那么无论数据结构如何,都不需要同步。

于 2013-02-16T03:58:12.333 回答
1

由于 tableList 不会被修改,它绝对是线程安全的

于 2013-02-16T03:48:59.373 回答
1

如果你只是打电话给吸气剂,你应该没问题。但是你所做的事情是有风险的,不是一个好的做法。

如果您担心“TableConnectionInfo”对象被修改,那么最好让它不可变。

于 2013-02-16T04:15:35.023 回答
1

为了安全起见,您需要在初始化和每个 getter 调用之间建立起之前发生的关系。在启动新线程之前,您在主线程中所做的任何事情都会发生 - 在新线程做任何事情之前发生,因此您的代码看起来是安全的。请参阅JLS。具体来说,“如果 x 和 y 是同一线程的操作,并且 x 在程序顺序中位于 y 之前,则为 hb(x, y)。” 和“线程上的 start() 调用发生在已启动线程中的任何操作之前。”

于 2013-02-16T04:24:24.347 回答
1

在我的运行方法中,我正在调用 TableConnectionInfo 类的 get 方法,所以它是否是线程安全的?

是的,它是线程安全的。

有两件事需要考虑。

  1. start()在每个线程上调用的主线程与线程方法的开始之间存在“发生在之前”的关系run()。这意味着主线程在启动子线程之前所做的状态更改(哈希表及其子/内容对象的创建和填充)将在run()开始执行时对子线程可见。

  2. 假设在那之后没有任何改变这些对象的状态,则不需要进一步的同步。


但是,您必须根据具体情况执行此分析。仅仅说一切正常是不够的,因为对象在某一点之后不会改变。事实上,如果start()/ run()happy-before 关系不存在,这将不是线程安全的。

于 2013-02-16T04:41:16.767 回答