容器本身已经提供了多线程的支持,为我们省下了并发方面的编码,但是并发通常会带来线程安全的问题,我们来讨论一下安全的问题。 先了解一个概念,属性:属性不同于parameter,它存在于request,session,context中,是以键值对儿的形式存在的,获取之后的值为object。 context中的线程安全。 其中的属性是非线程安全的,比如有两个servlet,A,B。 A中有如下代码: getServletContext().setAttribute("foo", "22"); getServletContext().setAttribute("bar", "42"); out.print(getServletContext().getAttribute("foo")); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } out.print(getServletContext().getAttribute("bar"));
这时页面上应该展示的为22,42,但是同时B中的代码为: getServletContext().setAttribute("bar", "16"); 在A执行到sleep的过程中,执行Bservlet就会发现结果变为22,16。这样就展示了线程的不安全。 处理方式: 因为是不同servelt的线程都能造成不安全,因此在servlet的方法service方法上使用synchronized关键字都是不能解决问题的,只能提升到context的高度来解决问题。 把A中的代码改为: synchronized(getServletContext()){ getServletContext().setAttribute("foo", "22"); getServletContext().setAttribute("bar", "42"); out.print(getServletContext().getAttribute("foo")); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } out.print(getServletContext().getAttribute("bar")); } 把B中的代码改为: synchronized(getServletContext()){ getServletContext().setAttribute("bar", "16"); out.print(getServletContext().getAttribute("bar")); } 就能够安全了 Session的线程安全 与context类似,也会存在不安全的问题,需要加入synchronized(session)就可以了。 Servlet的线程安全。 可能会有两个线程访问同一个servlet的情况,这样就会出现实例变量线程不安全的情况,如下: i为一个实例变量。 i++; try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("=========================" + i); 这里如果同时有两个客户端访问同一个servlet的时候就会出现问题。 为了解决这个问题,可以使用如下的办法。
实现SingleThreadModel接口,这样就能保证只能有一个线程访问servlet。但是就不能够并发了,影响效率。尽量使用临时变量,因为临时变量是存储在栈中的,而每一个线程都有一个独立的栈,所以不会有线程安全的问题。request的属性也是线程安全的。使用synchronized (this) {}代码块包住就可以了。