我有一个看起来像这样的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
映射传递给这样的各种线程,并且不会被任何线程修改(通过进行set调用)。每个线程都将使用get方法来获取所需的方法。
for (int i = 0; i< 1000; i++) {
service.submit(new Task(tableList));
}
下面是我的Task类,它强制运行Runnable Interface
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++;
}
}
}
}
问题: -
现在我的问题是 - 在我的run方法中,我调用了TableConnectionInfo
类的方法,所以它是否是线程安全的?因为多个线程会尝试进行调用。所以我不确定我是否需要在这里做额外的事情?或者代码看起来很好?
答案 0 :(得分:4)
Java中的引用值(和原语)赋值是原子的。您不必担心参考值处于某种半分配状态,就像您在较低级别语言中那样。
话虽如此,是的,在你的类中调用getter是线程安全的,因为它们只是访问不可变的String
对象但是......如果某个其他线程通过setter改变了一个你可能没有因缓存而获取当前值。您希望将String
中的所有TableConnectionInfo
变量声明为volatile
。这使得线程不会缓存旧值,因此getter会将其返回给您。
如果你的TableConnectionInfo
包含更多复杂的对象(例如List
或Map
s),而其他线程可能正在改变,那么吸气剂正在访问......那就是另一个故事。对可变,复杂数据结构的并发修改是不同的,并且需要同步。
哪个......指向你的tableList
。如果 可能正被另一个线程修改(添加/删除),那么您就遇到了问题。
如果您所做的只是读取(其他线程无法写入/修改),则无论数据结构如何,都不需要同步。
答案 1 :(得分:1)
由于tableList未被修改,因此绝对是线程安全的
答案 2 :(得分:1)
如果你只是打电话给吸气剂,你应该没问题。 但你所做的是冒险而且不是一种好的做法。
如果您担心“TableConnectionInfo”对象被修改,那么最好使其不可变。
答案 3 :(得分:1)
为了确保安全,您需要在初始化和每个getter调用之间建立先发生关系。在开始新线程之前在主线程中执行的任何操作都会发生在新线程执行之前,因此您的代码看起来很安全。请参阅JLS。具体来说,“如果x和y是同一个线程的动作,x在程序顺序中出现在y之前,那么hb(x,y)。”并且“在启动线程中的任何操作之前发生对线程的start()调用。”
答案 4 :(得分:1)
在我的run方法中,我调用了TableConnectionInfo类的方法,所以它是否是线程安全的?
是的,它是线程安全的。
有两件事需要考虑。
每个线程上调用start()
的主线程与线程'run()
方法的开头之间存在“之前发生”关系。这意味着在run()
开始执行时,子线程可以看到主线程在启动子线程之前所做的状态更改(哈希表及其子/内容对象的创建和填充)。
假设在该点之后没有任何内容改变这些对象的状态,则无需进一步同步。
但是,您必须根据具体情况执行此分析。并且说一切都没问题是不够的,因为对象在某一点之后不会改变。实际上,如果start()
/ run()
之前发生的关系不存在,那么这将不是线程安全的。