servlet 中 CopyOnArrayList 的线程安全

Thread safety with CopyOnArrayList in servlet

我刚开始学习 servlet 和线程。 Final 实例变量是线程安全的,CopyOnArrayList 也是如此。为什么以下代码不是线程安全的(它是最终的 + 我使用了 CopyOnArrayList)?

@WebServlet("/index.html")
public class CatServlet extends HttpServlet {
    private final static long serialVersionUID = 1L;
    private final static String VIEW = "/WEB-INF/JSP/index.jsp";
    **private final CopyOnWriteArrayList <Cat> l = new CopyOnWriteArrayList<>();**

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        l.add(new Cat("Dean", 7));
        l.add(new Cat("Sam", 7));
        l.add(new Cat("Pixie", 0));
        request.setAttribute("catList", l);
        request.getRequestDispatcher(VIEW).forward(request, response);
    }

JSP代码:

<c:forEach var="cat" items="${catList}" >
    <li>${cat.name}</li>
</c:forEach>

首先我得到了 3 个 Cat 实例。刷新时我得到 6,然后是 9、12 等等。为什么? 当我在 doGet 方法中声明 CopyOnWriteArrayList 时不会出现该问题,当我使用简单数组时也不会出现该问题。我不明白它背后的逻辑:最终实例变量和 CopyOnWriteArrayList 应该是线程安全的。谢谢大家的澄清。

您每次刷新页面时都会在列表中添加 3 个元素,因为您是在 doGet 方法中添加的。

如果您在 doGet 方法中声明列表,则每次调用该方法时都会将其删除。

这里不涉及线程安全。

CopyOnWriteArrayList,不是EraseOnWriteArrayListCopyOnWriteArrayList 每次写入都会创建一个新的副本,以便现有的迭代器继续在旧版本上运行。每次调用 doGet 时都不会删除该列表,因此它只是在最后添加。每次添加新的 Cat.

时,您只会有不必要的内部复制开销