构造函数是否已同步直到完全完成?

时间:2018-11-08 18:19:24

标签: java multithreading constructor synchronized

我正在构建一个程序,该程序需要构造一些需要如此大量计算才能创建的对象,我最明智的做法是将它们构建在自己的专用线程中,而主线程会继续从事其他工作,直到这些对象是必需的。

因此,我想到了创建一个专门设计用于在其自己的线程中创建自定义对象的特殊类。像这样:

public abstract class DedicatedThreadBuilder<T> {

    private T object;

    public DedicatedThreadBuilder() {
        DedicatedThread dt = new DedicatedThread(this);
        dt.start();
    }

    private void setObject(T i) {
        object = i;
    }

    protected abstract T constructObject();

    public synchronized T getObject() {
        return object;
    }

    private class DedicatedThread extends Thread {

        private DedicatedThreadBuilder dtb;

        public DedicatedThread(DedicatedThreadBuilder builder){
            dtb = builder;
        }

        public void run() {
            synchronized(dtb) {
                dtb.setObject(dtb.constructObject());
            }
        }

    }

}

我唯一担心的是,只有在主线程(即构成 DedicatedThreadBuilder 的线程)在 DedicatedThreadBuilder 上具有同步锁的情况下,该机制才能正常工作。构建完成,因此阻止了 DedicatedThread 构建产品对象的尝试,直到完成 DedicatedThreadBuilder 的构建为止。为什么?因为毫无疑问, DedicatedThreadBuilder 的子类将需要使用参数进行构造,因此需要将其传递到它们自己的私有存储中,以便可以在 constructObject()过程。

例如

public class JellybeanStatisticBuilder extends DedicatedThreadBuilder<JellybeanStatistics> {

    private int greens;
    private int blacks;
    private int yellows;

    public JellybeanStatisticBuilder(int g, int b, int y) {
        super();
        greens = g;
        blacks = b;
        yellows = y;
    }

    protected JellybeanStatistics constructObject() {
        return new JellybeanStatistics(greens, blacks, yellows);
    }

}

只有在对象被完全构造之后,该对象才能被其他线程阻塞,才能正常工作。否则, DedicatedThread 可能会在分配必要的变量之前尝试构建对象。

那么Java是如何工作的?

3 个答案:

答案 0 :(得分:2)

我认为您想要的是具有某种同步的工厂类:

bool is_valid(char ch, int base) {
    return isdigit(ch) && ch - '0' < base;
}

现在,您要用import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; public class SyncFactory<T> { // alternatively, use newCachedThreadPool or newFixedThreadPool if you want to allow some degree of parallel construction private ExecutorService executor = Executors.newSingleThreadExecutor(); public Future<T> create() { return executor.submit(() -> { return new T(); }); } } 替换在T准备就绪之前可能需要发生的T用法,并可以选择调用其Future<T>方法来阻止直到准备就绪,设置超时,或调用Future.get()来检查构造是否畅通无阻。此外,您可能不希望轮询Future,而是希望工厂调用回调或发布事件以在主线程完成构建时通知主线程。

答案 1 :(得分:0)

If (如果是)(确实是必须的)(首先考虑一个)...

您所追求的总体想法是可行的,但是您的代码令人困惑,无论如何,乍一看,我似乎认为它可能无效。这种可能会破坏事情的复杂性是沿着这条道路三思甚至三思的很好理由。

我立即发现的主要问题是您只创建了该对象的1个实例。如果这是一个仅在另一个线程上创建事物的工厂,则应该在DedicatedThread的{​​{1}}中而不是在其构造函数中调用DedicatedThreadBuilder

另一方面,如果您实际上打算让constructObject仅创建DedicatedThreadBuilder的1个实例,那么这种抽象似乎是不必要的...只需移动T的行为到DedicatedThread,因为DedicatedThreadBuilder似乎并没有做任何额外的事情。

其次,这不是一件很小的事情,它不是不正确,因为它只是不必要的:您有一个内部类,您可以将外部类的实例传递给其构造函数(即{ {1}}的构造函数引用其父DedicatedThreadBuilder)。这是不必要的,因为非静态内部类已经链接到其外部类,因此内部类可以引用外部类而无需任何额外的引用。

第三,如果将行为移出构造函数并移至单独的方法中,则可以对其进行同步。就个人而言,我本来会以DedicatedThread作为启动过程的对象,所以调用DedicatedThreadBuilder会开始创建对象,而constructObject本身将dtb.constructObject()设置为{完成。然后,您可以根据需要同步该方法,或者执行任何操作,而不必担心构造函数可能无法按照自己的方式进行操作-在我看来,您通常不必担心构造函数可能会有一些奇怪的副作用。 / p>

第四,您有什么办法知道对象何时准备就绪并可供获取?您可能需要为此添加一些机制,例如观察者或其他回调。

答案 2 :(得分:0)

问题是您在构造子类之前就在使用它。它实际上与多线程没有任何关系。如果您直接从constructObject构造函数调用DedicatedThreadBuilder,那同样会很糟糕。

最接近您的合理实施方式只是为DedicatedThreadBuidler提供一个单独的start()方法,该方法应在构造对象后调用。

或者您可以让它扩展Thread并使用Thread方法。

或者您可以让它实现Runnable,以便可以将其与ThreadExecutor或其他任何东西一起使用。