是否有必要同步最终成员?

时间:2013-09-20 09:59:55

标签: java multithreading synchronization

我有一个在不同线程中使用的数据库对象:

import java.sql.Connection;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;

public class Database
{
  private final PoolDataSource pool = PoolDataSourceFactory.getPoolDataSource();

  protected Connection connect ()
  {
    synchronized (pool) { return pool.getConnection(); }
  }

  protected void disconnect (Connection connection)
  {
    synchronized (pool) { connection.close(); }
  }
}

是否有必要同步对pool成员的访问权限?或者是否足以避免显式同步?

如果需要同步,代码将被破坏,因为没有必要调用disconnect来关闭连接。该类中的某些代码可以绕过同步调用connection.close()

3 个答案:

答案 0 :(得分:5)

通常,您不应使用互斥锁来保护对最终引用的访问。因为这类字段根据Java Memory Model安全发布。

在部分情况下PoolDataSourceImpl(或PoolXADataSourceImpl)能够更改自己的状态抛出方法getConnection()connection.close();。所以你应该检查一下你的PoolDataSource的实现是一个线程安全的。因此 Oracle® Database JDBC Developer's Guide and Reference看起来像DataSource是线程安全的,但不是Connection。

  

Oracle JDBC驱动程序提供全面支持,并且非常高   针对使用Java多线程的应用程序进行了优化。受控   对连接的串行访问,例如连接提供的连接   缓存,既是必要的,也是鼓励的。但是,Oracle强烈   不鼓励在多个线程之间共享数据库连接。   避免允许多个线程同时访问连接。   如果多个线程必须共享连接,请使用规范   开始使用/最终使用协议。

答案 1 :(得分:3)

仅当final字段不可变时才需要同步;例如,无需同步final String。如果你有一个不包含不可变对象的字段,你仍然需要同步,除非对象本身(如你的PoolDataSource)是线程安全的。

答案 2 :(得分:1)

可能没有必要。必须使用synchronized时有几个原因:

  1. 变量被更改为线程A并由线程B读取。

    final变量不会发生这种情况,所以这个原因就出来了。

  2. 您必须对多个变量进行更改,其他线程必须看不到或全部。

    访问单个字段不需要synchronized

  3. 您必须保护复杂对象的内部状态。 Map.put()就是这种情况。虽然x.put()只是一次访问,但地图内部状态的更新会更改多个变量,因此在这种情况下您需要synchronized

    问题是:您是否需要pool.getConnection()connection.close()

    您可以在您使用的库/框架的文档中找到答案。

  4. 一般来说,创建连接池是为了保留多线程应用程序的连接池,因此无需锁定就可以安全地调用它们。