美文网首页
面试题:什么叫线程安全?servlet是线程安全吗?

面试题:什么叫线程安全?servlet是线程安全吗?

作者: Petrel_Huang | 来源:发表于2020-04-27 01:12 被阅读0次

1. 什么是线程安全性问题

    在多线程同时访问同一段代码的情况下,无论线程怎么切换,怎么交互都不会出现脏读[1],丢失更新[2],代码都能正常运行,这样就称之为线程安全了。

2.出现线程安全问题的原因

    有线程安全,就存在线程不安全,那么我们接下来通过线程不安全的例子,来找到线程不安全的诱因。

线程不安全的例子如下:

public class UnsafetyDemo {
   public static void main(String[] args) {
       ShareResourse3 resourse = new ShareResourse3();
       new Thread(resourse).start();
       new Thread(resourse).start();
       new Thread(resourse).start();
   }
}
class ShareResourse3 implements Runnable {
   private int num = 0;

   public int getNum(){
       return ++num;
   }

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName() + "-->" + getNum());
   }
}

运行结果:

Thread-1-->1
Thread-2-->2
Thread-0-->1

上面代码运行不正常,出现了线程不安全,如果线程安全运行结果最后得到的num应该为2。

num++存在原子性问题(请参考上一篇文章《线程并发--原子变量解决自增自减原子性问题》),怎么验证呢?我们可以通过javap -verbose ShareResourse3.class命令反汇编查看ShareResourse3这个类的字节码,截取其中我们关心的getNum方法中的反汇编命令:

image.png image.png

分析如下:

    ①Thread-0线程抢到了时间片,线程开始执行,此时Thread-0获取到的num为0,执行到指令5的时候时间片用完,Thread-0运行暂停在了指令5,num还是为0.
    ②Thread-1线程抢到了时间片,开始执行代码,此时Thread-1获取到的num还是为0因为上一个线程没有改变num的值,Thread-1执行比较快,Thread-1执行完了并打印了之后时间片才用完,此时就在控制台上打印出了“Thread-1-->1”结果,而且num为1
    ③Thread-1线程时间片用完了,被Thread-2线程抢到了时间片Thread-2开始执行完毕并打印“Thread-2-->2”结果。此时num为2
    ④最后Thread-1和Thread-2都执行完了,没有线程抢时间片了,Thread-0重新抢到了时间片,从上次停的指令6开始运行,此时的num没有重新获取还是之前停下来的0,继续加1,为1,所以打印“Thread-0-->1”

通过上述例子,我们可以得到结论,导致线程不安全原因有以下三点:

多线程环境下
多个线程共享一个资源
对资源进行非原子性操作

3.servlet是线程安全吗?

答案:不安全的。我们用代码和结果来说明吧!

代码和结果如下:

public class ThreadServlet extends HttpServlet {
    private int i = 0;

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(this + "--->" + Thread.currentThread().getName() + "--->" + i);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        i++;
    }
}

运行结果:

image.png

    运行结果很显然,出现了线程不安全问题,我们一起来分析一下原理是什么?为什么servlet线程不安全?

servlet线程不安全原理:

    我们可以看到运行结果来,从结果上分析,很显然用户使用浏览器访问服务器中的servlet时,每一个http请求都是一个线程,访问多次就是多个线程访问ThreadServlet中的实例资源i,但是当Tomcat接收到Client的HTTP请求时,Tomcat只会创建一个servlet,因为servlet是单例的,那么很显然现在就是在多线程的情况下,多线程访问同一个资源,并且进行着非原子性操作。所以出现线程不安全。

如图所示:

image.png

4.解决Servlet线程问题有三种方式:

  1. 将非原子性代码放入到synchronized代码块中,但是synchronized代码块会大大降低性能。

  2. servlet实现 SingleThreadModel 接口,规定该servlet对象只能被一个线程访问,就不存在线程不安全了,但是SingleThreadModel 接口已经被定义为过时接口了不推荐使用,因为它是通过创建多个servlet对象,servlet不再是单例,而是一个servlet对应一个线程的方式,这样创建很多的servlet对象,浪费资源。

运行结果如下:

image.png
  1. 所以在servlet中尽量不使用实例变量,尽量不要共享内存资源就好了

【相关词汇】

[1]脏读:就是一个线程在修改还没有修改完,另外一个线程就将这个没有被修改完的数据读取了
[2]丢失更新:两个线程同时获取数据同时做修改,后面更新的线程会将前面的更新的线程更新的数据覆盖

相关文章

网友评论

      本文标题:面试题:什么叫线程安全?servlet是线程安全吗?

      本文链接:https://www.haomeiwen.com/subject/kjhoqqtx.html