实例字段访问是否应该在 Tapestry 页面或组件中同步?

Should instance fields access be synchronized in a Tapestry page or component?

如果页面或组件 class 有一个实例字段是非同步对象,f.ex。一个ArrayList,并且应用程序有代码在结构上修改这个字段,是否应该同步对这个字段的访问?

F.ex.:

public class MyPageOrComponent
{
    @Persist
    private List<String> myList;

    void setupRender()
    {
        if (this.myList == null)
        {
            this.myList = new ArrayList<>();
        }
    }

    void afterRender(MarkupWriter writer)
    {
        // Should this be synchronized ?

        if (someCondition)
        {
            this.myList.add(something);
        }
        else
        {
            this.myList.remove(something);
        }
    }
}

我问是因为我似乎明白 Tapestry 只创建页面或组件的一个实例 class 并且它为所有连接的客户端使用这个实例(但如果这不是真的请纠正我).

简而言之,答案是否定的,您不必这样做,因为 Tapestry 会为您做到这一点。 Tapestry 将在运行时为您转换您的页面和 类,无论您在哪里与字段交互,它们实际上都不会处理实例变量,而是处理线程安全的托管变量。完整的内部工作原理超出了我的范围,但可以找到对转换的简要参考 here

一个警告,不要在减速时实例化您的 page/component 变量。我已经看到一些奇怪的行为。所以不要这样做:

private List<String> myList = new ArrayList<String>;

Tapestry 使用一些运行时字节码魔术来转换您的页面和组件。页面和组件是单例,但属性已转换,因此它们由 PerThreadValue 支持。这意味着每个请求都会获得它自己的值副本,因此不需要同步。

正如@joostschouten 所建议的,您永远不应在字段声明中初始化可变 属性。他讨论的奇怪行为是因为这将被所有请求共享(因为初始化器只为 page/component 单例触发一次)。可变字段应该在渲染方法中初始化(例如@SetupRender)