聶永的博客

          記錄工作/學(xué)習(xí)的點(diǎn)點(diǎn)滴滴。

          Servlet 3.0筆記之Servlet單實(shí)例以及線程安全小結(jié)

          眾所周知,Servlet為單實(shí)例多線程,非線程安全的。

          若一個(gè)Servlet對應(yīng)多個(gè)URL映射,那么將會生成一個(gè)還是多個(gè)Servlet實(shí)例呢?

          最好的辦法就是動手體驗(yàn)一下

          @WebServlet({ "/demoServlet1", "/demoServlet2" })
          public class DemoServlet extends HttpServlet {
          private static final long serialVersionUID = 1L;

          protected void doGet(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {
          PrintWriter out = response.getWriter();
          out.println(request.getServletPath() + "[" + this + "]");
          out.flush();
          out.close();
          }
          }

          輸出結(jié)果:

          /demoServlet1[com.learn.servlet3.thread.DemoServlet@1320a41]
          /demoServlet2[com.learn.servlet3.thread.DemoServlet@9abce9]

          輸出結(jié)果可以看到映射/demoServlet1/demoServlet2對應(yīng)Servlet實(shí)例是不同的。

          結(jié)果證明:Servlet將為每一個(gè)URL映射生成一個(gè)實(shí)例;一個(gè)Servlet可能存在多個(gè)示例,但每一個(gè)實(shí)例都會對應(yīng)不同的URL映射。


          下面討論單個(gè)Servlet、多線程情況下保證數(shù)據(jù)線程同步的幾個(gè)方法。

          1. synchronized:代碼塊,方法
            大家都會使用的方式,不用詳細(xì)介紹了。
            建議優(yōu)先選擇修飾方法。
          2. volatile
            輕量級的鎖,可以保證多線程情況單線程讀取所修飾變量時(shí)將會強(qiáng)制從共享內(nèi)存中讀取最新值,但賦值操作并非原子性。
            一個(gè)具有簡單計(jì)數(shù)功能Servlet示范:
            /**
            * 使用Volatile作為輕量級鎖作為計(jì)數(shù)器
            *
            * @author yongboy
            * @date 2011-3-12
            * @version 1.0
            */
            @WebServlet("/volatileCountDemo")
            public class VolatileCountServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;

            private volatile int num = 0;

            protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
            addOne();
            response.getWriter().write("now access num : " + getNum());
            }

            /**
            * 讀取開銷低
            */
            private int getNum() {
            return num;
            }

            /**
            * 其寫入為非線程安全的,賦值操作開銷高
            */
            private synchronized void addOne() {
            num ++;
            }
            }
            我們在為volatile修飾屬性賦值時(shí),還是加把鎖的。
          3. ThreadLocal
            可以保證每一個(gè)線程都可以獨(dú)享一份變量副本,每個(gè)線程可以獨(dú)立改變副本,不會影響到其它線程。
            這里假設(shè)多線程環(huán)境一個(gè)可能落顯無聊的示范,初始化一個(gè)計(jì)數(shù),然后循環(huán)輸出:
            @WebServlet("/threadLocalServlet")
            public class ThreadLocalServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;

            private static ThreadLocal threadLocal = new ThreadLocal() {
            @Override
            protected Integer initialValue() {
            return 0;
            }
            };

            protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
            response.setHeader("Connection", "Keep-Alive");

            PrintWriter out = response.getWriter();
            out.println("start... " + " [" + Thread.currentThread() + "]");
            out.flush();

            for (int i = 0; i < 20; i++) {
            out.println(threadLocal.get());
            out.flush();

            threadLocal.set(threadLocal.get() + 1);
            }

            // 手動清理,當(dāng)然隨著當(dāng)前線程結(jié)束,亦會自動清理調(diào)
            threadLocal.remove();
            out.println("finish... ");
            out.flush();
            out.close();
            }
            }
            若創(chuàng)建一個(gè)對象較為昂貴,但又是非線程安全的,在某種情況下要求僅僅需要在線程中獨(dú)立變化,不會影響到其它線程。選擇使用ThreadLocal較好一些,嗯,還有,其內(nèi)部使用到了WeakHashMap,弱引用,當(dāng)前線程結(jié)束,意味著創(chuàng)建的對象副本也會被垃圾回收。
            Hibernate使用ThreadLocal創(chuàng)建Session;Spring亦用于創(chuàng)建對象會使用到一點(diǎn)。
            嗯,請注意這不是解決多線程共享變量的鑰匙,甚至你想讓某個(gè)屬性或?qū)ο笤谒芯€程中都保持原子性,顯然這不是解決方案。
          4. Lock
            沒什么好說的,現(xiàn)在JDK版本支持顯式的加鎖,相比synchronized,添加與釋放更加靈活,功能更為全面。
            @WebServlet("/lockServlet")
            public class LockServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;

            private static int num = 0;
            private static final Lock lock = new ReentrantLock();

            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            try{
            lock.lock();
            num ++;
            response.getWriter().println(num);
            }finally{
            lock.unlock();
            }
            }
            }
            必須手動釋放鎖,否則將會一直鎖定。
          5. wait/notify
            較老的線程線程同步方案,較之Lock,不建議再次使用。
          6. 原子操作
            原子包裝類,包括一些基本類型(int, long, double, boolean等)的包裝,對象屬性的包裝等。
            @WebServlet("/atomicServlet")
            public class AtomicServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;
            private static final AtomicInteger num = new AtomicInteger(0);

            protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
            PrintWriter out = response.getWriter();
            out.println(num.incrementAndGet());
            out.flush();
            out.close();
            }
            }
            包裝類提供了很多的快捷方法,比如上面的incrementAndGet方法,自身增加1,然后返回結(jié)果值,并且還是線程安全的,省缺了我們很多手動、笨拙的編碼實(shí)現(xiàn)。
            更多原子類,請參見 java.util.concurrent.atomic包。
          7. 一些建議
            盡量不要在Servlet中單獨(dú)啟用線程
            使用盡可能使用局部變量
            盡可能避免使用鎖
          8. 數(shù)據(jù)結(jié)構(gòu)小結(jié)
            在平常環(huán)境下使用的一些數(shù)據(jù)結(jié)構(gòu),在多線程并發(fā)環(huán)境下可選擇使用java.util.concurrent里內(nèi)置替代品。下面有一個(gè)小結(jié),僅供參考。
          非線程安全工具版JUC版本
          HashMap Collections.synchronizedMap ConcurrentHashMap
          ArrayList Collections.synchronizedList CopyOnWriteArrayList
          HashSet Collections.synchronizedSet synchronizedSet
          Queue   ConcurrentLinkedQueue



          Servlet線程安全有很太多的話題要說,以上僅僅為蜻蜓點(diǎn)水,真正需要學(xué)習(xí)和實(shí)踐的還有一段長路要學(xué)習(xí)。另,話說,這里已和Servlet版本無關(guān),是知識積累和分享。

          posted on 2011-03-14 13:17 nieyong 閱讀(6445) 評論(4)  編輯  收藏 所屬分類: Servlet3

          評論

          # re: Servlet 3.0筆記之Servlet單實(shí)例以及線程安全小結(jié)[未登錄] 2011-03-15 09:18 Jarod

          眾所周知servlet是線程安全的,線程不安全的是你自己的狀態(tài)數(shù)據(jù),與servlet完全無關(guān)  回復(fù)  更多評論   

          # re: Servlet 3.0筆記之Servlet單實(shí)例以及線程安全小結(jié) 2012-03-01 19:58 1111

          AnnotationServlet.doGet,Servlet: /AnnotationServlet servlet3.AnnotationServlet@5acb5e96
          AnnotationServlet.doGet,Servlet: /annotationServlet servlet3.AnnotationServlet@5acb5e96

          我的測試是不同URL,是同一個(gè)Servlet處理的哦..

          @WebServlet(urlPatterns ={"/annotationServlet","/AnnotationServlet"})
          public class AnnotationServlet extends HttpServlet
          {
          ...
          }  回復(fù)  更多評論   

          # re: Servlet 3.0筆記之Servlet單實(shí)例以及線程安全小結(jié) 2013-01-06 22:12 zelin

          /printServlet1[com.huawei.servlet.PrintServlet@136c1da]
          /printServlet1[com.huawei.servlet.PrintServlet@136c1da]
          朋友,我的輸出結(jié)果和你的不同啊。
          我的測試是不用URL,創(chuàng)建同一個(gè)Servlet。  回復(fù)  更多評論   

          # re: Servlet 3.0筆記之Servlet單實(shí)例以及線程安全小結(jié) 2015-08-20 10:17 浩哥

          應(yīng)該是單實(shí)例!樓主能不能將環(huán)境配置搞出來  回復(fù)  更多評論   

          公告

          所有文章皆為原創(chuàng),若轉(zhuǎn)載請標(biāo)明出處,謝謝~

          新浪微博,歡迎關(guān)注:

          導(dǎo)航

          <2011年3月>
          272812345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          統(tǒng)計(jì)

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個(gè)人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 三江| 哈巴河县| 乡城县| 樟树市| 湟源县| 津市市| 孙吴县| 苍梧县| 融水| 屯门区| 金塔县| 肃北| 固原市| 木里| 平罗县| 泾阳县| 革吉县| 咸丰县| 永修县| 阿荣旗| 商洛市| 内乡县| 页游| 金堂县| 永吉县| 旅游| 荥阳市| 通江县| 汉寿县| 襄垣县| 喜德县| 航空| 浦城县| 昌宁县| 沙湾县| 秦皇岛市| 耒阳市| 同仁县| 永清县| 绥芬河市| 新密市|