由于Servlet是单例的,而一个Servlet对象可能同时处理多个请求,因此Servlet是线程不安全的。

@WebServlet(name = "ThreadServlet", urlPatterns = "/ThreadServlet")
public class ThreadServlet extends HttpServlet {
    String name;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        String threadName = Thread.currentThread().getName();
        name = threadName;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "________" + name);
    }
}

以上案例代码,在多线程中会出现线程不安全,因为多个线程同时访问的是同一个成员变量,会共享该实例变量,而在访问局部变量时,每个线程都会有自己的变量,不会被共享。
故,servlet在多线程中不安全

解决Servlet线程安全问题

1、最简单的方式,不使用成员变量,使用局部变量即可

@WebServlet(name = "ThreadServlet2", urlPatterns = "/ThreadServlet2")
public class ThreadServlet2 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        String name;
        String threadName = Thread.currentThread().getName();
        name = threadName;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "________" + name);
    }
}

2、使用synchronized关键字加锁来解决servlet线程安全问题

@WebServlet(name = "ThreadServlet3", urlPatterns = "/ThreadServlet3")
public class ThreadServlet3 extends HttpServlet {
    String name;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        synchronized (this) {
            String threadName = Thread.currentThread().getName();
            name = threadName;
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "________" + name);
        }
    }
}

3、使用ThreadLocal来解决线程不安全

@WebServlet(name = "ThreadServlet4", urlPatterns = "/ThreadServlet4")
public class ThreadServlet4 extends HttpServlet {
    String name;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        ThreadLocal<String> local = new ThreadLocal<>();
        String threadName = Thread.currentThread().getName();
        name = threadName;
        local.set(name);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "________" + local.get());
    }
}

总结:在Servlet线程安全问题中,尽可能不创建成员变量。

Q.E.D.


如人饮水、冷暖自知