如何保证对象的所有非线程安全引用都将被同步?

时间:2017-01-10 09:14:39

标签: java multithreading synchronization thread-safety

假设我有一个班级:

public final class Server {

private final ArrayList<ServerConnection> connections;
private ServerConnection pending;
private Thread connector;

public Server() {
    connections = new ArrayList<>();

    connector = new Thread(() -> {
        while (true) {
            pending = new ServerConnection();
            pending.waitForConnection();

            //Could be adding while another thread is iterating.
            connections.add(pending);
        }
    }, "Connection Establisher");
    connector.setDaemon(true);
    connector.setPriority(Thread.MIN_PRIORITY);
    connector.start();
}

//Anyone with a refrence to this object can access connections.
public ArrayList<ServerConnection> getConnections() {
    return connections;
}
}

在添加对象时,如何确保connections未被使用?我想过在线程中使用synchronized (connections) {...}块但是根据我对synchronized块的了解,对connections的所有非线程安全引用都必须在同步块中。有什么方法可以确保所有对connections的非线程安全访问都是同步的吗?

1 个答案:

答案 0 :(得分:0)

使getConnections方法同步是不够的,因为一旦调用者获得对列表的引用,它就可以随意执行任何操作,包括线程不安全操作。

一些简单的步骤可以使您的代码更加健壮:

  • 仅使用最终变量
  • 提供启动和停止方法。此时,您在构造函数中启动线程并泄漏对此的引用 - 这可能会产生奇怪的可见性效果。并且你不管理线程停止,使线程成为守护进程 - 它可以工作,但可能不那么干净。

简单的重写可能看起来像下面的代码(它当然可以改进) - 检查注释。请注意,如果waitForConnection也适当地对中断作出反应,例如通过抛出InterruptedException,它会更好。

public final class Server {

  //us a thread safe list
  private final List<ServerConnection> connections = new CopyOnWriteArrayList<>();
  //make the thread final
  private final Thread connector;

  public Server() {
    connector = new Thread(() -> {
      //provide a mechanism to stop the thread: exit on interruption
      while (!Thread.currentThread().isInterrupted()) { 
        ServerConnection pending = new ServerConnection();
        pending.waitForConnection();

        //Could be adding while another thread is iterating.
        connections.add(pending);
      }
    }, "Connection Established");
    //Note that the priority may be ignored at runtime
    connector.setPriority(Thread.MIN_PRIORITY);
  }

  public void start() {
    connector.start();
  }

  //to stop the thread, interrupt it
  public void stop() {
    if (!connector.isAlive()) throw new IllegalStateException("The server is not started");
    connector.interrupt();
  }

  //don't return the list but an unmodifiable view of the list
  public List<ServerConnection> getConnections() {
    return Collections.unmodifiableList(connections);
  }
}