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いますが、どのスレッドによっても(set呼び出しを行うことによって)変更されることはありません。各スレッドはgetメソッドを使用して必要なメソッドを取得します。

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

以下は、RunnableInterfaceを実装する私のタスククラスです

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での参照値(およびそのことについてはプリミティブ)の割り当てはアトミックです。低水準言語の場合のように、参照値がある種の半分割り当てられた状態にあることを心配する必要はありません。

そうは言っても、クラスでゲッターを呼び出すことは、String不変のオブジェクトにアクセスしているだけなので、スレッドセーフですが...他のスレッドがセッターを介してオブジェクトを変更した場合、キャッシュのために現在の値を取得できない可能性があります。Stringすべての変数をTableConnectionInfoとして宣言しますvolatile。これにより、スレッドが古い値をキャッシュして、ゲッターがそれを返すのを防ぎます。

他のスレッドが変更されている可能性があるときにゲッターがアクセスしていたTableConnectionInfoより複雑なオブジェクト(Listsやsなど)が含まれている場合、それは別の話です。Map可変で複雑なデータ構造への同時変更は異なり、同期が必要になります。

どの...あなたの方を指していますtableListそれが別のスレッド(追加/削除)によって変更されている可能性がある場合は、問題があります。

読み取りのみを行っている場合(別のスレッドが書き込み/変更を行っている可能性はありません)、データ構造に関係なく同期は必要ありません。

于 2013-02-16T03:58:12.333 に答える
1

ゲッターを呼んでいるだけなら大丈夫です。しかし、あなたがしていることは危険であり、良い習慣ではありません。

「TableConnectionInfo」オブジェクトが変更されることを心配している場合は、不変にすることをお勧めします。

于 2013-02-16T04:15:35.023 に答える
1

tableListは変更されないため、完全にスレッドセーフです。

于 2013-02-16T03:48:59.373 に答える
1

安全にするには、初期化と各 getter 呼び出しの間に事前発生関係が必要です。新しいスレッドを開始する前にメイン スレッドで行うことはすべて、新しいスレッドが行う前に発生するため、コードは安全に見えます。JLSを参照してください。具体的には、「x と y が同じスレッドのアクションであり、プログラムの順序で x が y の前にある場合、hb(x, y)」です。および「スレッドでの start() の呼び出しは、開始されたスレッド内のアクションの前に発生します。」

于 2013-02-16T04:24:24.347 に答える
1

run メソッドで、TableConnectionInfo クラスの get メソッドを呼び出しているので、スレッドセーフかどうか。

はい、スレッドセーフです。

考慮すべき点が 2 つあります。

  1. start()各スレッドのメイン スレッド呼び出しとスレッドのrun()メソッドの開始との間には、「前に発生する」関係があります。run()これは、子スレッドを開始する前にメイン スレッドによって行われた状態変更 (ハッシュ テーブルとその子/コンテンツ オブジェクトの作成と設定) が、実行を開始する時点で子スレッドに表示されることを意味します。

  2. その時点以降、これらのオブジェクトの状態が何も変更されないと仮定すると、それ以上の同期は必要ありません。


ただし、この分析はケースバイケースで実行する必要があります。そして、オブジェクトは特定の時点以降変化しないため、すべてが OK であると言うだけでは十分ではありません。実際、start()/run()発生前の関係が存在しない場合、これはスレッドセーフではありません。

于 2013-02-16T04:41:16.767 に答える