隨筆-4  評論-15  文章-0  trackbacks-0
            2009年4月24日

          此時程序中最多有幾個存活的線程,個人認為應(yīng)該是4個,怎么就是看不到是4個的結(jié)果呢?
          package Test;

          public class Testthread{
               public static void main(String[] args){

                //測定當前活動的線程數(shù)目 肯定有一個主線程在運行
                System.out.println("=========header========"+Thread.activeCount());
                ThreadTest tt=new ThreadTest();  
                RunTest rt=new RunTest();
          //在下面新建線程里被啟動

                new Thread(rt).start();      //新建立的一個線程 啟動
                tt.start();                        //已經(jīng)建立的線程 啟動
                System.out.println("===========footer======"+Thread.activeCount());
           }
          }
          class ThreadTest extends Thread{
           public void run(){
            System.out.println("==========I'm over thread=========");
           }
          }

          class RunTest implements Runnable{
           public void run(){
            System.out.println("==========I'm over runnable==========");
           }
          }


          打印結(jié)果:
          第一類結(jié)果 (次序有時不同)
          =========header========1
          ===========footer======3
          ==========I'm over thread=========
          ==========I'm over runnable==========

          第二類結(jié)果
          =========header========1
          ==========I'm over runnable==========
          ===========footer======2
          ==========I'm over thread=========


          posted @ 2009-04-24 15:04 王業(yè)平 閱讀(1171) | 評論 (6)編輯 收藏
          Servlet的生命周期:

          (1)裝載Servlet。這項操作一般是動態(tài)執(zhí)行的。然而,Server通常會提供一個管理的選項,用于在Server啟動時強制裝載和初始化特定的Servlet;

          (2)Server創(chuàng)建一個Servlet的實例;

          (3)Server調(diào)用Servlet的init()方法;

          (4)一個客戶端的請求到達Server;

          (5)Server創(chuàng)建一個請求對象;

          (6)Server創(chuàng)建一個響應(yīng)對象;

          (7)Server激活Servlet的service()方法,傳遞請求和響應(yīng)對象作為參數(shù);

          (8)service()方法獲得關(guān)于請求對象的信息,處理請求,訪問其他資源,獲得需要的信息;

          (10)service()方法使用響應(yīng)對象的方法,將響應(yīng)傳回Server、最終到達客戶端。

          service()方法可能激活其它方法以處理請求,如doGet()或doPost()或程序員自己開發(fā)的新的方法;

          對于更多的客戶端請求,Server創(chuàng)建新的請求和響應(yīng)對象,仍然激活此Servlet的service()方法,將這兩個對象作為參數(shù)傳遞給它。如此重復以上的循環(huán),但無需再次調(diào)用init()方法。一般Servlet只初始化一次;

          當Server不再需要Servlet時,Server調(diào)用Servlet的Destroy()方法。
          posted @ 2009-04-24 10:13 王業(yè)平 閱讀(1223) | 評論 (2)編輯 收藏
            2009年4月23日
          不知如何轉(zhuǎn)帖,只有ctrl +v了

          Servlet線程安全探討

          閱讀(67) 評論(0) 發(fā)表時間:2008年11月20日 15:46

          本文地址:http://qzone.qq.com/blog/190658200-1227167200

          本文標簽: Servlet 線程安全
          我們在開發(fā)JAVA WEB應(yīng)用程序,大多都會考慮用MVC模式的框架來部署(相信沒有程序員再考慮前兩者簡單的模式了吧。),這也是基于下面的原因:
          一、MVC由于分層清晰,容易看到整個系統(tǒng)流程的架構(gòu),對于越來越復雜的系統(tǒng)是有相當大的幫助。
          二、 擴展性,耦合性低。各層影響相當少,如JSP頁面只負責數(shù)據(jù)顯示,M負責業(yè)務(wù)邏輯處理,C相當于Servlet來控制流程的轉(zhuǎn)向。等等這一系列的好處。
          MVC的模式,其本質(zhì)就是用Servlet的應(yīng)用技術(shù)。Servlet/jsp和其他如ASP\PHP語言相比,由于使用了多線程運行技術(shù)與具有很高的執(zhí)行效率。但是也就是Servlet由于默認多線程模式執(zhí)行,依我們所了解線程安全性問題,也就不得不要考慮在Servlet中也存在這樣的問題,然而,很多程序員只專注于業(yè)務(wù)邏輯的處理,并沒有注意到多線程的安全性的問題(在此編寫之前,我也存在這樣的經(jīng)歷,不過還好。。。。),這往往造成編寫的程序在用戶量少的時候沒出什么問題,而一旦發(fā)現(xiàn)大量的并發(fā)用戶時,而且這數(shù)量達到一定的數(shù)量時,就會出現(xiàn)一系列莫名的問題,這問題在下面我們可以看的到。
          Servlet的多線程機制是怎么樣的呢:
          Servlet體系結(jié)構(gòu)是建立在JAVA的多線程機制上的,但它的生命周期是由WEB容器來管理的,當客戶端第一次請求某個Servlet時,Servlet容器會根據(jù)web.xml的配置實例化相應(yīng)的Servlet類,當有新的客戶端來請求這個Servlet時,容器一般不會再實例化這個Servlet類,而是以線程方式去調(diào)用這個實例的方法,然后再有更多的客戶端來請求時,就存在了多個線程在使用這個實例。并且Servlet容器會自動使用線程池技術(shù)來支持系統(tǒng)的運行。
          在這樣的情況,當兩個或者多個客戶端同時請求同一Serlvet時,就會存在多個線程同時訪問同一資源的情況,數(shù)據(jù)就可能變的不一致,所以在用Servlet搭建WEB應(yīng)用程序時如果不考慮線程的問題,就會出現(xiàn)難以發(fā)現(xiàn)的問題。
          Servlet線程安全問題的例子:
          Servlet線程是由于使用實例變量不當而導致的,這里有如下的例子:
          代碼程序如下:
          public class SecurityTest extends HttpServlet {
              PrintWriter output;//成員變量

              @Override
              protected void service(HttpServletRequest request,
                     HttpServletResponse response) throws ServletException,
          IOException {
                 response.setContentType("text/html;charset=gb2312");
                 String name = request.getParameter("name");
                 output=response.getWriter();
                 try {
                     Thread.sleep(5000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 output.write(name);
              }

          }

          該實例中定義了一個實例變量output,在service方法中負責輸出用戶名,當一個用戶訪問該Servlet時,程序會正常的運行,但當多個用戶并發(fā)的訪問時,就可能會出現(xiàn)其他的用戶信息顯示在另一個用戶的瀏覽器上的問題,為了看到實際的效果,在這個程序中,我特做如下的處理:就是延時5000毫秒,讓第一個用戶暫停在輸出數(shù)據(jù)前。然后我們馬上發(fā)起另一個請求,這種情況下就會出現(xiàn)如下的頁面
          第一次請求:
          http://127.0.0.1:8080/Test2/securityTest?name=a

          大家看到這頁面什么數(shù)據(jù)也沒有,就是說那姓名沒有打印出來,那跑到哪里去了呢?看第二個用戶請求的情況。
          第二個用戶請求地址:http://127.0.0.1:8080/Test2/securityTest?name=b

          可以看到,原來a值已經(jīng)打印到第二個用戶的瀏覽器了。
          可以想像在暫停5000時間里,第二個用戶請求這個servlet,已經(jīng)把output的引用變成了第二個用戶請求的output值了,這樣就解釋了為什么會輸出到第二個客戶端的瀏覽的原因。

          從內(nèi)存模型來看Servlet的線程安全問題//不是很理解這一段
             JAVA的內(nèi)存模型JMM(JAVA Memory Model)主要是為了規(guī)定線程與內(nèi)存的一些關(guān)系,既然Servlet也是用線程技術(shù),那么我們也從這方面尋找根本原因,根據(jù)JMM,系統(tǒng)存在有主內(nèi)存,JAVA的實例變量(就是類變量吧)都是存在于主內(nèi)存供其他內(nèi)存使用,也就是對所有的線程都是共享的,而根據(jù)線程的特點:它是有自己的工作內(nèi)存的,工作內(nèi)存包括緩存和堆棧兩部分,堆棧是專門用來存在方法中的局部變量的,而緩存則是主內(nèi)存中變量的拷貝,緩存與主內(nèi)存并不總是同步的,也就是緩存中的變量的修改可能沒有立刻寫到主存中,如下圖:

          根據(jù)內(nèi)存模型,我們可以得到如下的線程調(diào)度表:
          調(diào)度時刻
          a線程
          b線程
          T1
          訪問Servlet頁面
          T2
          訪問Servlet頁面
          T3
          output=a的輸出username=a休眠5000毫秒,讓出CPU
          T4
          output=b的輸出(寫回主存)username=b休眠5000毫秒,讓出CPU
          T5
          在用戶b的瀏覽器上輸出a線程的username的值,a線程終止。
          T6
          在用戶b的瀏覽器上輸出b線程的username的值,b線程終止。

          可以看出,由于b線程對實例變量output的修改覆蓋了a線程對實例變量output的值,直接導致了用戶a的信息顯示到b的瀏覽器上。根據(jù)內(nèi)存模型,我們也可以解釋到,正是因為b開始時修改了緩存中的output值,然后刷新到主內(nèi)存中,而又有足夠的時間刷新到a緩存中,這時aOutput值就直接導致了指向b瀏覽器。
          解決方法:
          從上面的分析中,我們知道導致線程不安全的主要原因在于實例變量的使用不當,下面就提出如下三種解決方法
          第一,Servlet類實現(xiàn)SingleThreadModel接口,該接口指定系統(tǒng)如何處理對同一個Servlet的調(diào)用,如果一個Servlet被指定實現(xiàn)這個接口,那么,在這個Servlet中的service方法將不會在兩個線程中同時執(zhí)行,也就是說執(zhí)行完一個后再執(zhí)行下一個請求的service,當然也就不存在線程不安全的問題了。
          代碼如下:
          public class SecurityTest extends HttpServlet implements SingleThreadModel{}
          其實這方法也就相當于是同步方法的效果吧。

          第二.同步對共享數(shù)據(jù)的操作。我們所熟悉的就是用synchronized關(guān)鍵字,這樣能保證一次只有一個線程來操作被保護的區(qū)段。在本例子也可以用synchronized來保證線程的安全,代碼如下:
          public class SecurityTest extends HttpServlet {
              PrintWriter output;

              @Override
              protected void service(HttpServletRequest request,
                     HttpServletResponse response) throws ServletException, IOException {
                 response.setContentType("text/html;charset=gb2312");
                 String name = request.getParameter("name");

          Synchronized(this){
                 output=response.getWriter();
                 try {
                     Thread.sleep(5000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 output.write(name);
          }
              }}
          第三:避免使用實例變量,而使用局部變量。因為Servlet線程不安全的原因是由實例變量引起,所以我們可以避免使用實例變量,而使用局部變量,線程之間很難直接訪問局部變量 ,這樣就從根本上解決了這一問題。
          在本例子中,就是將output放在service方法中當局部變量 。
          public class SecurityTest extends HttpServlet {

              @Override
              protected void service(HttpServletRequest request,
                     HttpServletResponse response) throws ServletException, IOException {
                  PrintWriter output;
                 response.setContentType("text/html;charset=gb2312");
                 String name = request.getParameter("name");
                 output=response.getWriter();
                 try {
                     Thread.sleep(5000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 output.write(name);
              }}

          這三種方法都對解決servlet線程安全起到很好的作用,但我們?nèi)绻麑λ麄冞M行比較一下,看哪一種更適合呢:
          第一個方案中:實現(xiàn)SingleThreadModel接口,Servlet引擎將為每個客戶請求都生成一個Servlet實例,這將引起大量的系統(tǒng)開銷,在新版本的Servlet2.4中也不提倡使用了。
          第二個方案中:在程序中使用同步來保護要使用的共享數(shù)據(jù),也使系統(tǒng)的性能大大的下降,這是因為被同步的代碼在同一時刻只能由一個線程來執(zhí)行,使得同時處理其他客戶請求的吞吐量大大降低,大量客戶處于阻塞狀態(tài),這對于并發(fā)用戶請求來說并非是一件很好的事情。另外為了保持主內(nèi)存與工作內(nèi)存數(shù)據(jù)的一致性要頻繁地刷新緩存,這也大大降低了系統(tǒng)性能,所以這種方案不大可取。
          第三個方案則應(yīng)該是最優(yōu)方案:從JAVA內(nèi)存模型來看,方法中的臨時變量都是在棧中分配空間,而每個線程都有自己的私有棧空間,互不干擾,不會影響性能,也不會產(chǎn)生線程安全的問題。
          posted @ 2009-04-23 16:31 王業(yè)平 閱讀(399) | 評論 (0)編輯 收藏
          現(xiàn)在要求輸入一個文件的目錄,之后將里面所有的備份文件刪除,備份文件都是以“.bak”或".BAK"結(jié)尾

          package TestFile;

          import java.io.File;
          import java.io.FileFilter;

          /**
           * @author 王業(yè)平
           * 用于過濾以.bak結(jié)尾或包含.bak的文件
           * 返回值的含義是 當包含該字符串時返回true
           */

          public class ListFilter implements FileFilter{
           @Override
           public boolean accept(File file) {

              //測試指定的文件(夾)是否應(yīng)該包含在指定的列表中(就是測定作為過濾的條件是否滿足)
            /*
             * 如果是目錄的話,直接返回true 表示是滿足條件的一情況
             */
            if(file.isDirectory()) return true;
            /*
             * 如果不是目錄的話,通過判斷在其名字里是否含有規(guī)定的字符,
             * 因為string的index方法在不滿足條件(不含有指定的字符)是返回-1
             * 所以以此來確定是否滿足條件
             */
            String name=file.getName();
              /*
               * int index=name.indexOf(".bak"); 
               *
           return index!=-1;
            * 這種方法不安全,可能會刪除文件名中還含有.bak的文件,備份文件還可以用.BAK結(jié)尾
              */
              return name.endsWith(".bak")||name.endsWith(".BAK");

           }
          }


          使用此類

          package TestFile;

          import java.io.File;
          /**
           * @author Administrator
           * 涉及到遞歸調(diào)用
           */

          public class BakDelete{
           public static void main(String[] args){
            BakDelete bd=new BakDelete();
            bd.listBakFile(new File("E:\\"));
           }
           public void listBakFile(File file){
            File[] fs=file.listFiles(new ListFilter());
            for(int i=0;i<fs.length;i++){
             if(fs[i].isFile()){
              System.out.println(fs[i].getAbsolutePath());//打印文件絕對路徑
              fs[i].delete();
             }else{
              //System.out.println(fs[i].getAbsolutePath());
              listBakFile(fs[i]);
             }    
            }
            //System.out.println(fs.length);
           }
          }

          posted @ 2009-04-23 13:54 王業(yè)平 閱讀(1243) | 評論 (7)編輯 收藏
          僅列出標題  
          主站蜘蛛池模板: 景洪市| 大邑县| 页游| 亳州市| 彰化县| 华蓥市| 晋州市| 东安县| 阿克苏市| 扎赉特旗| 太和县| 仪征市| 吉首市| 汶上县| 老河口市| 金乡县| 卫辉市| 霍山县| 卢龙县| 贞丰县| 蓝田县| 仙桃市| 广饶县| 定西市| 平南县| 化隆| 新田县| 普洱| 盘锦市| 望城县| 丰台区| 壤塘县| 安新县| 抚松县| 那坡县| 丹寨县| 西畴县| 工布江达县| 四子王旗| 巢湖市| 乌拉特前旗|