codefans

          導(dǎo)航

          <2005年9月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678

          統(tǒng)計(jì)

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          程序設(shè)計(jì)鏈接

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          2005年9月6日 #

          09年的一月

            一年又過去了,習(xí)慣上我還是使用陰歷來判別一年,元旦過后,我還沒有想到一年又過去了,但是過完春節(jié),這種感覺就突然特別強(qiáng)烈起來,呵呵,小時(shí)候養(yǎng)成的習(xí)慣,不是說變就能改變的。
             在一月份怎么說呢,上半個(gè)月感覺特別累,人也特別敏感,易怒。下半個(gè)月,還是相對(duì)比較輕松的心態(tài),就算是犒勞自己吧。第一二周主要是準(zhǔn)備工作匯報(bào)材料,一群人沒日沒夜地干,就為了把工作匯報(bào)整出色, 還額外新增了許多功能,光說短信吧, 短短3天,就集成進(jìn)了系統(tǒng),這個(gè)不容易啊,每個(gè)人都鉚足了勁,想整點(diǎn)色彩出來,當(dāng)然我其實(shí)也知道,我說過12月會(huì)輕松一點(diǎn),但是12月份沒有輕松過,然后我又說1月份會(huì)輕松一點(diǎn),接著就是更累得一月份,自己也感覺慚愧啊,項(xiàng)目本來工作量太多是一方面,但是更多的,我還是認(rèn)為,管理上面還是存在很大問題,沒有預(yù)見到兩個(gè)重大問題,第一,框架的嚴(yán)重不足,第二,人員培訓(xùn)嚴(yán)重不足,當(dāng)時(shí)對(duì)框架依賴程度過高, 其實(shí)框架在后面幾次致命的變動(dòng),給系統(tǒng)帶來的修改工作量非同一般?。粚?duì)人員的水平估計(jì)過高,也是把所有人包括自己弄得很慘的一個(gè)重要原因,以為經(jīng)過前面的一個(gè)小項(xiàng)目,人員水平應(yīng)該都有很大的提高,滿足一般的項(xiàng)目需求問題不是很大的,但是實(shí)際情況告訴我,從一個(gè)不懂編程到熟練工, 絕對(duì)不是一蹴而就的,就算是勞動(dòng)強(qiáng)度很大的鍛煉,那也需要很長(zhǎng)時(shí)間的,至少也需要一到兩年。我們只用了2個(gè)月,就算是學(xué)了九陽(yáng)神功也沒有那么快啊。當(dāng)然所有的一切責(zé)任歸結(jié)起來,就是項(xiàng)目管理的失敗,風(fēng)險(xiǎn)識(shí)別錯(cuò)誤,一開始的風(fēng)險(xiǎn)識(shí)別就只是考慮時(shí)間緊,業(yè)務(wù)不熟練,根本就沒有考慮到人員的技術(shù)水平,所以后面對(duì)質(zhì)量的忽視的默許,對(duì)人員培養(yǎng)的中斷, 直接導(dǎo)致了最后的一個(gè)大泥潭,當(dāng)然我們現(xiàn)在是甲方,這個(gè)跳蚤還是得自己摘掉的,因?yàn)槟悴皇歉梢黄本妥呷说陌ⅰ?br />   在匯報(bào)后,就開始進(jìn)行年終總結(jié), 呵呵,因?yàn)橛泻芏喔邢耄?jì)劃的三千字,輕易的就達(dá)到,寫得時(shí)候根本就沒有考慮過達(dá)到了。

          針對(duì)需要改進(jìn)的內(nèi)容也寫了很多,有幾點(diǎn)特別重要,第一,千萬(wàn)不要深度參與到項(xiàng)目的開發(fā)中去,因?yàn)檫@樣你害了所有人,沒有統(tǒng)籌安排,必然導(dǎo)致更多的問題和混亂,而且這些內(nèi)容遲早要交接出去的,那么,別人接手還是很痛苦,所以從一開始就要把握好自己。第二,計(jì)劃很重要,計(jì)劃要仔細(xì)考慮,不是一拍腦袋,計(jì)劃就出來,完成了很多電子表格,但是這沒有用,有用的計(jì)劃應(yīng)該是討論出來的,是仔細(xì)思考,全盤考慮的結(jié)果。最后,計(jì)劃始終只是計(jì)劃,還需要不斷跟進(jìn),這樣才能逐步逼近目標(biāo)。

          下月還有一些計(jì)劃,主要一個(gè)是制定項(xiàng)目管理規(guī)范,很多時(shí)候做到有章可循還是比較好的,最佳實(shí)踐不一定完全適合你,但是你總能受點(diǎn)啟發(fā)吧。日積月累,不就形成了一個(gè)所以得經(jīng)驗(yàn)了嗎?打游戲還講究經(jīng)驗(yàn)值呢,做事為什么就要拒絕經(jīng)驗(yàn)?zāi)亍?br /> 第二個(gè),還要把現(xiàn)在的內(nèi)容消化,現(xiàn)在內(nèi)容很多,但老實(shí)說,需要花力氣整合,而且還有些核心功能都不是特別穩(wěn)定,也急切需要穩(wěn)定?,F(xiàn)在一堆所謂的重構(gòu), 但是我一直知道有一句話,沒有最好,只有最合適。其實(shí)對(duì)于軟件也是這樣,優(yōu)化是沒有止境的,但是我們也要有一個(gè)度,再過三個(gè)月,回過頭來看,現(xiàn)在的很多功能和代碼又會(huì)有點(diǎn)像狗屎,因?yàn)槟氵M(jìn)步了。是不是我們?cè)倩獍阉鼈兌贾貥?gòu)一下呢,不一定。首先用戶對(duì)于你的修改是否認(rèn)為一定需要,用的還蠻好的,也總的給我一個(gè)更換的理由吧, 總不能說廠家都從n70換到n73, 我就要換吧。其次,你的時(shí)間和精力允許你這樣做嗎?但是并不是代表我們就什么都不做,一直拒絕新事物,這樣怎么能從量變到質(zhì)變呢,不質(zhì)變,不適應(yīng)新情況了總是要被淘汰的吧,比如小靈通。

          所以我想我們每個(gè)人都要習(xí)慣改變,但是要抓住本質(zhì),有一句話“要么改變,要么被改變”。

          posted @ 2009-02-04 21:07 春雷的博客 閱讀(281) | 評(píng)論 (0)編輯 收藏

          防止緩存

          1, 使用java提供的方法,在jsp或者servlet中都可以
          <%
          response.setHeader("Pragma","No-cache");
          response.setHeader("Cache-Control","no-cache");
          response.setDateHeader("Expires", 0);
          %>
          2, 使用HTML標(biāo)記,如下面:
          <HEAD>
          <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
          <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
          <META HTTP-EQUIV="Expires" CONTENT="0">
          </HEAD>  

          posted @ 2006-01-12 12:40 春雷的博客 閱讀(318) | 評(píng)論 (0)編輯 收藏

          int、char、double與byte相互轉(zhuǎn)換的程序

          轉(zhuǎn)載  http://www.matrix.org.cn/thread.shtml?forum_id=19&view_id=919
          int、char、double與byte相互轉(zhuǎn)換的程序
          //整數(shù)到字節(jié)數(shù)組的轉(zhuǎn)換
            public static byte[] intToByte(int number) {
              int temp = number;
              byte[] b=new byte[4];
              for (int i=b.length-1;i>-1;i--){
                b[i] = new Integer(temp&0xff).byteValue();      //將最高位保存在最低位
                temp = temp >> 8;       //向右移8位
              }
              return b;
            }

            //字節(jié)數(shù)組到整數(shù)的轉(zhuǎn)換
            public static int byteToInt(byte[] b) {
              int s = 0;
              for (int i = 0; i < 3; i++) {
                if (b[i] >= 0)
                  s = s + b[i];
                else


                  s = s + 256 + b[i];
                s = s * 256;
              }
              if (b[3] >= 0)       //最后一個(gè)之所以不乘,是因?yàn)榭赡軙?huì)溢出
                s = s + b[3];
              else
                s = s + 256 + b[3];
              return s;
            }

            //字符到字節(jié)轉(zhuǎn)換
            public static byte[] charToByte(char ch){
              int temp=(int)ch;
              byte[] b=new byte[2];
              for (int i=b.length-1;i>-1;i--){
                b[i] = new Integer(temp&0xff).byteValue();      //將最高位保存在最低位
                temp = temp >> 8;       //向右移8位
              }
              return b;
            }

            //字節(jié)到字符轉(zhuǎn)換


            public static char byteToChar(byte[] b){
              int s=0;
              if(b[0]>0)
                s+=b[0];
              else
                s+=256+b[0];
              s*=256;
              if(b[1]>0)
                s+=b[1];
              else
                s+=256+b[1];
              char ch=(char)s;
              return ch;
            }

            //浮點(diǎn)到字節(jié)轉(zhuǎn)換
            public static byte[] doubleToByte(double d){
              byte[] b=new byte[8];
              long l=Double.doubleToLongBits(d);
              for(int i=0;i<b.length;i++){
                b[i]=new Long(l).byteValue();
                l=l>>8;


              }
              return b;
            }

            //字節(jié)到浮點(diǎn)轉(zhuǎn)換
            public static double byteToDouble(byte[] b){
              long l;

              l=b[0];
              l&=0xff;
              l|=((long)b[1]<<8);
              l&=0xffff;
              l|=((long)b[2]<<16);
              l&=0xffffff;
              l|=((long)b[3]<<24);
              l&=0xffffffffl;
              l|=((long)b[4]<<32);
              l&=0xffffffffffl;

              l|=((long)b[5]<<40);
              l&=0xffffffffffffl;
              l|=((long)b[6]<<48);


              l|=((long)b[7]<<56);
              return Double.longBitsToDouble(l);
            }

          --

          posted @ 2005-11-25 12:42 春雷的博客 閱讀(926) | 評(píng)論 (0)編輯 收藏

          Java 多線程入門大全

           
          先從線程的創(chuàng)建說起.線程的創(chuàng)建一共有兩種形式:

          --------------------------------------------------------------------------------

              一種是繼承自Thread類.Thread 類是一個(gè)具體的類,即不是抽象類,該類封裝了線程的行為.要?jiǎng)?chuàng)建一個(gè)線程,程序員必須創(chuàng)建一個(gè)從 Thread 類導(dǎo)出的新類.程序員通過覆蓋 Thread 的 run() 函數(shù)來完成有用的工作.用戶并不直接調(diào)用此函數(shù);而是通過調(diào)用 Thread 的 start() 函數(shù),該函數(shù)再調(diào)用 run().
              
              例如:

              public class Test extends Thread{
                public Test(){
                }
                public static void main(String args[]){
                  Test t1 = new Test();
                  Test t2 = new Test();
                  t1.start();
                  t2.start();
                }
                public void run(){
                  //do thread's things
                }
              }

          --------------------------------------------------------------------------------

              
              另一種是實(shí)現(xiàn)Runnable接口,此接口只有一個(gè)函數(shù),run(),此函數(shù)必須由實(shí)現(xiàn)了此接口的類實(shí)現(xiàn).
              
              例如:

              public class Test implements Runnable{
                Thread thread1;
                Thread thread2;
                public Test(){
                  thread1 = new Thread(this,"1");
                  thread2 = new Thread(this,"2");
                }
                public static void main(String args[]){
                  Test t = new Test();
                  t.startThreads();
                }
                public void run(){
                  //do thread's things
                }
                public void startThreads(){
                  thread1.start();
                  thread2.start();
                }
              }

              兩種創(chuàng)建方式看起來差別不大,但是弄不清楚的話,也許會(huì)將你的程序弄得一團(tuán)糟.兩者區(qū)別有以下幾點(diǎn):

          1.當(dāng)你想繼承某一其它類時(shí),你只能用后一種方式.

          2.第一種因?yàn)槔^承自Thread,只創(chuàng)建了自身對(duì)象,但是在數(shù)量上,需要幾個(gè)線程,就得創(chuàng)建幾個(gè)自身對(duì)象;第二種只創(chuàng)建一個(gè)自身對(duì)象,卻創(chuàng)建幾個(gè)Thread對(duì)象.而兩種方法重大的區(qū)別就在于此,請(qǐng)你考慮:如果你在第一種里創(chuàng)建數(shù)個(gè)自身對(duì)象并且start()后,你會(huì)發(fā)現(xiàn)好像synchronized不起作用了,已經(jīng)加鎖的代碼塊或者方法居然同時(shí)可以有幾個(gè)線程進(jìn)去,而且同樣一個(gè)變量,居然可以有好幾個(gè)線程同時(shí)可以去更改它.(例如下面的代碼)這是因?yàn)?在這個(gè)程序中,雖然你起了數(shù)個(gè)線程,可是你也創(chuàng)建了數(shù)個(gè)對(duì)象,而且,每個(gè)線程對(duì)應(yīng)了每個(gè)對(duì)象也就是說,每個(gè)線程更改和占有的對(duì)象都不一樣,所以就出現(xiàn)了同時(shí)有幾個(gè)線程進(jìn)入一個(gè)方法的現(xiàn)象,其實(shí),那也不是一個(gè)方法,而是不同對(duì)象的相同的方法.所以,這時(shí)候你要加鎖的話,只能將方法或者變量聲明為靜態(tài),將static加上后,你就會(huì)發(fā)現(xiàn),線程又能管住方法了,同時(shí)不可能有兩個(gè)線程進(jìn)入同樣一個(gè)方法,那是因?yàn)?現(xiàn)在不是每個(gè)對(duì)象都擁有一個(gè)方法了,而是所有的對(duì)象共同擁有一個(gè)方法,這個(gè)方法就是靜態(tài)方法.

              而你如果用第二種方法使用線程的話,就不會(huì)有上述的情況,因?yàn)榇藭r(shí),你只創(chuàng)建了一個(gè)自身對(duì)象,所以,自身對(duì)象的屬性和方法對(duì)于線程來說是共有的.

              因此,我建議,最好用后一種方法來使用線程.

          public class mainThread extends Thread{
            int i=0;
            public static void main(String args[]){
              mainThread m1 = new mainThread();
              mainThread m2 = new mainThread();
              mainThread m3 = new mainThread();
              mainThread m4 = new mainThread();
              mainThread m5 = new mainThread();
              mainThread m6 = new mainThread();
              m1.start();
              m2.start();
              m3.start();
              m4.start();
              m5.start();
              m6.start();
            }
            public synchronized void t1(){
              i=++i;
              try{

          Thread.sleep(500);
              }
              catch(Exception e){}
              //每個(gè)線程都進(jìn)入各自的t1()方法,分別打印各自的i
              System.out.println(Thread.currentThread().getName()+" "+i);
            }
            public void run(){
              synchronized(this){
                while (true) {
                  t1();
                }
              }
            }
          }

          --------------------------------------------------------------------------------

              下面我們來講synchronized的4種用法吧:

              1.方法聲明時(shí)使用,放在范圍操作符(public等)之后,返回類型聲明(void等)之前.即一次只能有一個(gè)線程進(jìn)入該方法,其他線程要想在此時(shí)調(diào)用該方法,只能排隊(duì)等候,當(dāng)前線程(就是在synchronized方法內(nèi)部的線程)執(zhí)行完該方法后,別的線程才能進(jìn)入.

                例如:

                public synchronized void synMethod() {
                  //方法體
                }

              2.對(duì)某一代碼塊使用,synchronized后跟括號(hào),括號(hào)里是變量,這樣,一次只有一個(gè)線程進(jìn)入該代碼塊.例如:

                public int synMethod(int a1){
                  synchronized(a1) {
                    //一次只能有一個(gè)線程進(jìn)入
                  }
                }
              3.synchronized后面括號(hào)里是一對(duì)象,此時(shí),線程獲得的是對(duì)象鎖.例如:

          public class MyThread implements Runnable {
            public static void main(String args[]) {
              MyThread mt = new MyThread();
              Thread t1 = new Thread(mt, "t1");
              Thread t2 = new Thread(mt, "t2");
              Thread t3 = new Thread(mt, "t3");
              Thread t4 = new Thread(mt, "t4");
              Thread t5 = new Thread(mt, "t5");
              Thread t6 = new Thread(mt, "t6");
              t1.start();
              t2.start();
              t3.start();
              t4.start();
              t5.start();
              t6.start();
            }

            public void run() {
              synchronized (this) {
                System.out.println(Thread.currentThread().getName());
              }
            }
          }


              對(duì)于3,如果線程進(jìn)入,則得到對(duì)象鎖,那么別的線程在該類所有對(duì)象上的任何操作都不能進(jìn)行.在對(duì)象級(jí)使用鎖通常是一種比較粗糙的方法.為什么要將整個(gè)對(duì)象都上鎖,而不允許其他線程短暫地使用對(duì)象中其他同步方法來訪問共享資源?如果一個(gè)對(duì)象擁有多個(gè)資源,就不需要只為了讓一個(gè)線程使用其中一部分資源,就將所有線程都鎖在外面.由于每個(gè)對(duì)象都有鎖,可以如下所示使用虛擬對(duì)象來上鎖:

          class FineGrainLock {

             MyMemberClass x, y;
             Object xlock = new Object(), ylock = new Object();

             public void foo() {
                synchronized(xlock) {
                   //access x here
                }

                //do something here - but don't use shared resources

                synchronized(ylock) {
                   //access y here
                }
             }

             public void bar() {
                synchronized(this) {
                   //access both x and y here
                }
                //do something here - but don't use shared resources
             }
          }



              4.synchronized后面括號(hào)里是類.例如:

          class ArrayWithLockOrder{
            private static long num_locks = 0;
            private long lock_order;
            private int[] arr;

            public ArrayWithLockOrder(int[] a)
            {
              arr = a;
              synchronized(ArrayWithLockOrder.class) {//-----------------------------------------這里
                num_locks++;             // 鎖數(shù)加 1.
                lock_order = num_locks;  // 為此對(duì)象實(shí)例設(shè)置唯一的 lock_order.
              }
            }
            public long lockOrder()
            {
              return lock_order;
            }
            public int[] array()
            {
              return arr;
            }
          }

          class SomeClass implements Runnable

          {
            public int sumArrays(ArrayWithLockOrder a1,
                                 ArrayWithLockOrder a2)
            {
              int value = 0;
              ArrayWithLockOrder first = a1;       // 保留數(shù)組引用的一個(gè)
              ArrayWithLockOrder last = a2;        // 本地副本.
              int size = a1.array().length;
              if (size == a2.array().length)
              {
                if (a1.lockOrder() > a2.lockOrder())  // 確定并設(shè)置對(duì)象的鎖定
                {                                     // 順序.
                  first = a2;
                  last = a1;
                }
                synchronized(first) {              // 按正確的順序鎖定對(duì)象.
                  synchronized(last) {
                    int[] arr1 = a1.array();
                    int[] arr2 = a2.array();
                    for (int i=0; i<size; i++)
                      value += arr1[i] + arr2[i];
                  }
                }
              }
              return value;
            }
            public void run() {
              //...
            }
          }



              對(duì)于4,如果線程進(jìn)入,則線程在該類中所有操作不能進(jìn)行,包括靜態(tài)變量和靜態(tài)方法,實(shí)際上,對(duì)于含有靜態(tài)方法和靜態(tài)變量的代碼塊的同步,我們通常用4來加鎖.

          以上4種之間的關(guān)系:

              鎖是和對(duì)象相關(guān)聯(lián)的,每個(gè)對(duì)象有一把鎖,為了執(zhí)行synchronized語(yǔ)句,線程必須能夠獲得synchronized語(yǔ)句中表達(dá)式指定的對(duì)象的鎖,一個(gè)對(duì)象只有一把鎖,被一個(gè)線程獲得之后它就不再擁有這把鎖,線程在執(zhí)行完synchronized語(yǔ)句后,將獲得鎖交還給對(duì)象.
              在方法前面加上synchronized修飾符即可以將一個(gè)方法聲明為同步化方法.同步化方法在執(zhí)行之前獲得一個(gè)鎖.如果這是一個(gè)類方法,那么獲得的鎖是和聲明方法的類相關(guān)的Class類對(duì)象的鎖.如果這是一個(gè)實(shí)例方法,那么此鎖是this對(duì)象的鎖.

          --------------------------------------------------------------------------------

            下面談一談一些常用的方法:

            wait(),wait(long),notify(),notifyAll()等方法是當(dāng)前類的實(shí)例方法,
              
                  wait()是使持有對(duì)象鎖的線程釋放鎖;
                  wait(long)是使持有對(duì)象鎖的線程釋放鎖時(shí)間為long(毫秒)后,再次獲得鎖,wait()和wait(0)等價(jià);
                  notify()是喚醒一個(gè)正在等待該對(duì)象鎖的線程,如果等待的線程不止一個(gè),那么被喚醒的線程由jvm確定;
                  notifyAll是喚醒所有正在等待該對(duì)象鎖的線程.
                  在這里我也重申一下,我們應(yīng)該優(yōu)先使用notifyAll()方法,因?yàn)閱拘阉芯€程比喚醒一個(gè)線程更容易讓jvm找到最適合被喚醒的線程.

              對(duì)于上述方法,只有在當(dāng)前線程中才能使用,否則報(bào)運(yùn)行時(shí)錯(cuò)誤java.lang.IllegalMonitorStateException: current thread not owner.

          --------------------------------------------------------------------------------

              下面,我談一下synchronized和wait(),notify()等的關(guān)系:

          1.有synchronized的地方不一定有wait,notify

          2.有wait,notify的地方必有synchronized.這是因?yàn)閣ait和notify不是屬于線程類,而是每一個(gè)對(duì)象都具有的方法,而且,這兩個(gè)方法都和對(duì)象鎖有關(guān),有鎖的地方,必有synchronized.

          另外,請(qǐng)注意一點(diǎn):如果要把notify和wait方法放在一起用的話,必須先調(diào)用notify后調(diào)用wait,因?yàn)槿绻{(diào)用完wait,該線程就已經(jīng)不是current thread了.如下例:

          /**
          * Title:        Jdeveloper's Java Projdect
          * Description:  n/a
          * Copyright:    Copyright ? 2001
          * Company:      soho  http://www.ChinaJavaWorld.com
          * @author jdeveloper@21cn.com
          * @version 1.0
          */
          import java.lang.Runnable;
          import java.lang.Thread;

          public class DemoThread
              implements Runnable {

            public DemoThread() {
              TestThread testthread1 = new TestThread(this, "1");

          TestThread testthread2 = new TestThread(this, "2");

              testthread2.start();
              testthread1.start();

            }

            public static void main(String[] args) {
              DemoThread demoThread1 = new DemoThread();

            }

            public void run() {

              TestThread t = (TestThread) Thread.currentThread();
              try {
                if (!t.getName().equalsIgnoreCase("1")) {
                  synchronized (this) {
                    wait();
                  }
                }
                while (true) {

                  System.out.println("@time in thread" + t.getName() + "=" +
                                     t.increaseTime());

                  if (t.getTime() % 10 == 0) {
                    synchronized (this) {
                      System.out.println("****************************************");
                      notify();
                      if (t.getTime() == 100)
                        break;
                      wait();
                    }
                  }
                }
              }
              catch (Exception e) {
                e.printStackTrace();
              }
            }

          }

          class TestThread
              extends Thread {
            private int time = 0;
            public TestThread(Runnable r, String name) {
              super(r, name);
            }

            public int getTime() {
              return time;
            }

            public int increaseTime() {
              return++time;
            }

          }

              下面我們用生產(chǎn)者/消費(fèi)者這個(gè)例子來說明他們之間的關(guān)系:

              public class test {
            public static void main(String args[]) {
              Semaphore s = new Semaphore(1);
              Thread t1 = new Thread(s, "producer1");
              Thread t2 = new Thread(s, "producer2");
              Thread t3 = new Thread(s, "producer3");
              Thread t4 = new Thread(s, "consumer1");
              Thread t5 = new Thread(s, "consumer2");
              Thread t6 = new Thread(s, "consumer3");
              t1.start();
              t2.start();
              t3.start();
              t4.start();
              t5.start();
              t6.start();
            }
          }

          class Semaphore
              implements Runnable {
            private int count;
            public Semaphore(int n) {
              this.count = n;
            }

            public synchronized void acquire() {
              while (count == 0) {
                try {
                  wait();
                }
                catch (InterruptedException e) {
                  //keep trying
                }
              }
              count--;
            }

            public synchronized void release() {
              while (count == 10) {
                try {
                  wait();
                }
                catch (InterruptedException e) {
                  //keep trying
                }
              }
              count++;
              notifyAll(); //alert a thread that's blocking on this semaphore
            }

            public void run() {
              while (true) {
                if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("consumer")) {
                  acquire();
                }
                else if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("producer")) {
                  release();
                }
                System.out.println(Thread.currentThread().getName() + " " + count);
              }
            }
          }

                 生產(chǎn)者生產(chǎn),消費(fèi)者消費(fèi),一般沒有沖突,但當(dāng)庫(kù)存為0時(shí),消費(fèi)者要消費(fèi)是不行的,但當(dāng)庫(kù)存為上限(這里是10)時(shí),生產(chǎn)者也不能生產(chǎn).請(qǐng)好好研讀上面的程序,你一定會(huì)比以前進(jìn)步很多.

                上面的代碼說明了synchronized和wait,notify沒有絕對(duì)的關(guān)系,在synchronized聲明的方法,代碼塊中,你完全可以不用wait,notify等方法,但是,如果當(dāng)線程對(duì)某一資源存在某種爭(zhēng)用的情況下,你必須適時(shí)得將線程放入等待或者喚醒.

           

           

          ------------------------

          在java中,每個(gè)對(duì)象只有一個(gè)相應(yīng)的monitor,一個(gè)mutex,而每一個(gè)monitor都可以有多個(gè)“doors”可以進(jìn)入,即,同一個(gè)monitor中被守護(hù)的代碼可以在不同的地方,因?yàn)橥粋€(gè)對(duì)象可以出現(xiàn)在不同的代碼段,只要mutex鎖定的對(duì)象是同一個(gè),每個(gè)入口都用Synchronized關(guān)鍵字表明,當(dāng)一個(gè)線程通過了Synchronized關(guān)鍵字,它就所住了該monitor所有的doors。因此是mutex定義了monitor而不是代碼。

          另外,wait和notify、notifyAll都是Object的方法,使用wait必須是The current thread must own this object's monitor
          wait
          public final void wait()
                          throws InterruptedExceptionCauses current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).
          The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

          This method should only be called by a thread that is the owner of this object's monitor. See the notify method for a description of the ways in which a thread can become the owner of a monitor.


          Throws:
          IllegalMonitorStateException - if the current thread is not the owner of the object's monitor.
          InterruptedException - if another thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
          See Also:
          notify(), notifyAll()

          A thread becomes the owner of the object's monitor in one of three ways:

          By executing a synchronized instance method of that object.
          By executing the body of a synchronized statement that synchronizes on the object.
          For objects of type Class, by executing a synchronized static method of that class.
          Only one thread at a time can own an object's monitor.

            

          posted @ 2005-11-23 18:27 春雷的博客 閱讀(722) | 評(píng)論 (0)編輯 收藏

          2005.11.14

          瑟瑟北風(fēng)落葉零,冷冷秋雨窗前聽。晚來陣陣涼意襲,夜深唙唙芭蕉響。

          posted @ 2005-11-15 16:54 春雷的博客 閱讀(304) | 評(píng)論 (0)編輯 收藏

          最近找到一個(gè)很不錯(cuò)介紹session的文章

           

          關(guān)鍵詞:Session

           標(biāo)題:Session詳解 
          --------------------------------------------------------------------------------
          [評(píng)論]

          作者:郎云鵬(dev2dev ID: hippiewolf)

          摘要:雖然session機(jī)制在web應(yīng)用程序中被采用已經(jīng)很長(zhǎng)時(shí)間了,但是仍然有很多人不清楚session機(jī)制的本質(zhì),以至不能正確的應(yīng)用這一技術(shù)。本文將詳細(xì)討論session的工作機(jī)制并且對(duì)在Java web application中應(yīng)用session機(jī)制時(shí)常見的問題作出解答。

          目錄:
          一、術(shù)語(yǔ)session
          二、HTTP協(xié)議與狀態(tài)保持
          三、理解cookie機(jī)制
          四、理解session機(jī)制
          五、理解javax.servlet.http.HttpSession
          六、HttpSession常見問題
          七、跨應(yīng)用程序的session共享
          八、總結(jié)
          參考文檔

          一、術(shù)語(yǔ)session
          在我的經(jīng)驗(yàn)里,session這個(gè)詞被濫用的程度大概僅次于transaction,更加有趣的是transaction與session在某些語(yǔ)境下的含義是相同的。

          session,中文經(jīng)常翻譯為會(huì)話,其本來的含義是指有始有終的一系列動(dòng)作/消息,比如打電話時(shí)從拿起電話撥號(hào)到掛斷電話這中間的一系列過程可以稱之為一個(gè) session。有時(shí)候我們可以看到這樣的話“在一個(gè)瀏覽器會(huì)話期間,...”,這里的會(huì)話一詞用的就是其本義,是指從一個(gè)瀏覽器窗口打開到關(guān)閉這個(gè)期間 ①。最混亂的是“用戶(客戶端)在一次會(huì)話期間”這樣一句話,它可能指用戶的一系列動(dòng)作(一般情況下是同某個(gè)具體目的相關(guān)的一系列動(dòng)作,比如從登錄到選購(gòu)商品到結(jié)賬登出這樣一個(gè)網(wǎng)上購(gòu)物的過程,有時(shí)候也被稱為一個(gè)transaction),然而有時(shí)候也可能僅僅是指一次連接,也有可能是指含義①,其中的差別只能靠上下文來推斷②。

          然而當(dāng)session一詞與網(wǎng)絡(luò)協(xié)議相關(guān)聯(lián)時(shí),它又往往隱含了“面向連接”和/或“保持狀態(tài)”這樣兩個(gè)含義, “面向連接”指的是在通信雙方在通信之前要先建立一個(gè)通信的渠道,比如打電話,直到對(duì)方接了電話通信才能開始,與此相對(duì)的是寫信,在你把信發(fā)出去的時(shí)候你并不能確認(rèn)對(duì)方的地址是否正確,通信渠道不一定能建立,但對(duì)發(fā)信人來說,通信已經(jīng)開始了。“保持狀態(tài)”則是指通信的一方能夠把一系列的消息關(guān)聯(lián)起來,使得消息之間可以互相依賴,比如一個(gè)服務(wù)員能夠認(rèn)出再次光臨的老顧客并且記得上次這個(gè)顧客還欠店里一塊錢。這一類的例子有“一個(gè)TCP session”或者 “一個(gè)POP3 session”③。

          而到了web服務(wù)器蓬勃發(fā)展的時(shí)代,session在web開發(fā)語(yǔ)境下的語(yǔ)義又有了新的擴(kuò)展,它的含義是指一類用來在客戶端與服務(wù)器之間保持狀態(tài)的解決方案④。有時(shí)候session也用來指這種解決方案的存儲(chǔ)結(jié)構(gòu),如“把xxx保存在session 里”⑤。由于各種用于web開發(fā)的語(yǔ)言在一定程度上都提供了對(duì)這種解決方案的支持,所以在某種特定語(yǔ)言的語(yǔ)境下,session也被用來指代該語(yǔ)言的解決方案,比如經(jīng)常把Java里提供的javax.servlet.http.HttpSession簡(jiǎn)稱為session⑥。

          鑒于這種混亂已不可改變,本文中session一詞的運(yùn)用也會(huì)根據(jù)上下文有不同的含義,請(qǐng)大家注意分辨。
          在本文中,使用中文“瀏覽器會(huì)話期間”來表達(dá)含義①,使用“session機(jī)制”來表達(dá)含義④,使用“session”表達(dá)含義⑤,使用具體的“HttpSession”來表達(dá)含義⑥

          二、HTTP協(xié)議與狀態(tài)保持
          HTTP 協(xié)議本身是無狀態(tài)的,這與HTTP協(xié)議本來的目的是相符的,客戶端只需要簡(jiǎn)單的向服務(wù)器請(qǐng)求下載某些文件,無論是客戶端還是服務(wù)器都沒有必要紀(jì)錄彼此過去的行為,每一次請(qǐng)求之間都是獨(dú)立的,好比一個(gè)顧客和一個(gè)自動(dòng)售貨機(jī)或者一個(gè)普通的(非會(huì)員制)大賣場(chǎng)之間的關(guān)系一樣。

          然而聰明(或者貪心?)的人們很快發(fā)現(xiàn)如果能夠提供一些按需生成的動(dòng)態(tài)信息會(huì)使web變得更加有用,就像給有線電視加上點(diǎn)播功能一樣。這種需求一方面迫使HTML逐步添加了表單、腳本、DOM等客戶端行為,另一方面在服務(wù)器端則出現(xiàn)了CGI規(guī)范以響應(yīng)客戶端的動(dòng)態(tài)請(qǐng)求,作為傳輸載體的HTTP協(xié)議也添加了文件上載、 cookie這些特性。其中cookie的作用就是為了解決HTTP協(xié)議無狀態(tài)的缺陷所作出的努力。至于后來出現(xiàn)的session機(jī)制則是又一種在客戶端與服務(wù)器之間保持狀態(tài)的解決方案。

          讓我們用幾個(gè)例子來描述一下cookie和session機(jī)制之間的區(qū)別與聯(lián)系。筆者曾經(jīng)常去的一家咖啡店有喝5杯咖啡免費(fèi)贈(zèng)一杯咖啡的優(yōu)惠,然而一次性消費(fèi)5杯咖啡的機(jī)會(huì)微乎其微,這時(shí)就需要某種方式來紀(jì)錄某位顧客的消費(fèi)數(shù)量。想象一下其實(shí)也無外乎下面的幾種方案:
          1、該店的店員很厲害,能記住每位顧客的消費(fèi)數(shù)量,只要顧客一走進(jìn)咖啡店,店員就知道該怎么對(duì)待了。這種做法就是協(xié)議本身支持狀態(tài)。
          2、發(fā)給顧客一張卡片,上面記錄著消費(fèi)的數(shù)量,一般還有個(gè)有效期限。每次消費(fèi)時(shí),如果顧客出示這張卡片,則此次消費(fèi)就會(huì)與以前或以后的消費(fèi)相聯(lián)系起來。這種做法就是在客戶端保持狀態(tài)。
          3、發(fā)給顧客一張會(huì)員卡,除了卡號(hào)之外什么信息也不紀(jì)錄,每次消費(fèi)時(shí),如果顧客出示該卡片,則店員在店里的紀(jì)錄本上找到這個(gè)卡號(hào)對(duì)應(yīng)的紀(jì)錄添加一些消費(fèi)信息。這種做法就是在服務(wù)器端保持狀態(tài)。

          由于HTTP協(xié)議是無狀態(tài)的,而出于種種考慮也不希望使之成為有狀態(tài)的,因此,后面兩種方案就成為現(xiàn)實(shí)的選擇。具體來說cookie機(jī)制采用的是在客戶端保持狀態(tài)的方案,而session機(jī)制采用的是在服務(wù)器端保持狀態(tài)的方案。同時(shí)我們也看到,由于采用服務(wù)器端保持狀態(tài)的方案在客戶端也需要保存一個(gè)標(biāo)識(shí),所以session機(jī)制可能需要借助于cookie機(jī)制來達(dá)到保存標(biāo)識(shí)的目的,但實(shí)際上它還有其他選擇。

          三、理解cookie機(jī)制 
          cookie機(jī)制的基本原理就如上面的例子一樣簡(jiǎn)單,但是還有幾個(gè)問題需要解決:“會(huì)員卡”如何分發(fā);“會(huì)員卡”的內(nèi)容;以及客戶如何使用“會(huì)員卡”。

          正統(tǒng)的cookie分發(fā)是通過擴(kuò)展HTTP協(xié)議來實(shí)現(xiàn)的,服務(wù)器通過在HTTP的響應(yīng)頭中加上一行特殊的指示以提示瀏覽器按照指示生成相應(yīng)的cookie。然而純粹的客戶端腳本如JavaScript或者VBScript也可以生成cookie。

          而cookie 的使用是由瀏覽器按照一定的原則在后臺(tái)自動(dòng)發(fā)送給服務(wù)器的。瀏覽器檢查所有存儲(chǔ)的cookie,如果某個(gè)cookie所聲明的作用范圍大于等于將要請(qǐng)求的資源所在的位置,則把該cookie附在請(qǐng)求資源的HTTP請(qǐng)求頭上發(fā)送給服務(wù)器。意思是麥當(dāng)勞的會(huì)員卡只能在麥當(dāng)勞的店里出示,如果某家分店還發(fā)行了自己的會(huì)員卡,那么進(jìn)這家店的時(shí)候除了要出示麥當(dāng)勞的會(huì)員卡,還要出示這家店的會(huì)員卡。

          cookie的內(nèi)容主要包括:名字,值,過期時(shí)間,路徑和域。
          其中域可以指定某一個(gè)域比如.google.com,相當(dāng)于總店招牌,比如寶潔公司,也可以指定一個(gè)域下的具體某臺(tái)機(jī)器比如www.google.com或者froogle.google.com,可以用飄柔來做比。
          路徑就是跟在域名后面的URL路徑,比如/或者/foo等等,可以用某飄柔專柜做比。
          路徑與域合在一起就構(gòu)成了cookie的作用范圍。
          如果不設(shè)置過期時(shí)間,則表示這個(gè)cookie的生命期為瀏覽器會(huì)話期間,只要關(guān)閉瀏覽器窗口,cookie就消失了。這種生命期為瀏覽器會(huì)話期的 cookie被稱為會(huì)話cookie。會(huì)話cookie一般不存儲(chǔ)在硬盤上而是保存在內(nèi)存里,當(dāng)然這種行為并不是規(guī)范規(guī)定的。如果設(shè)置了過期時(shí)間,瀏覽器就會(huì)把cookie保存到硬盤上,關(guān)閉后再次打開瀏覽器,這些cookie仍然有效直到超過設(shè)定的過期時(shí)間。

          存儲(chǔ)在硬盤上的cookie 可以在不同的瀏覽器進(jìn)程間共享,比如兩個(gè)IE窗口。而對(duì)于保存在內(nèi)存里的cookie,不同的瀏覽器有不同的處理方式。對(duì)于IE,在一個(gè)打開的窗口上按 Ctrl-N(或者從文件菜單)打開的窗口可以與原窗口共享,而使用其他方式新開的IE進(jìn)程則不能共享已經(jīng)打開的窗口的內(nèi)存cookie;對(duì)于 Mozilla Firefox0.8,所有的進(jìn)程和標(biāo)簽頁(yè)都可以共享同樣的cookie。一般來說是用javascript的window.open打開的窗口會(huì)與原窗口共享內(nèi)存cookie。瀏覽器對(duì)于會(huì)話cookie的這種只認(rèn)cookie不認(rèn)人的處理方式經(jīng)常給采用session機(jī)制的web應(yīng)用程序開發(fā)者造成很大的困擾。

          下面就是一個(gè)goolge設(shè)置cookie的響應(yīng)頭的例子
          HTTP/1.1 302 Found
          Location: http://www.google.com/intl/zh-CN/
          Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
          Content-Type: text/html




          這是使用HTTPLook這個(gè)HTTP Sniffer軟件來俘獲的HTTP通訊紀(jì)錄的一部分




          瀏覽器在再次訪問goolge的資源時(shí)自動(dòng)向外發(fā)送cookie

           


          使用Firefox可以很容易的觀察現(xiàn)有的cookie的值
          使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。




          IE也可以設(shè)置在接受cookie前詢問

           


          這是一個(gè)詢問接受cookie的對(duì)話框。

          四、理解session機(jī)制
          session機(jī)制是一種服務(wù)器端的機(jī)制,服務(wù)器使用一種類似于散列表的結(jié)構(gòu)(也可能就是使用散列表)來保存信息。

          當(dāng)程序需要為某個(gè)客戶端的請(qǐng)求創(chuàng)建一個(gè)session的時(shí)候,服務(wù)器首先檢查這個(gè)客戶端的請(qǐng)求里是否已包含了一個(gè)session標(biāo)識(shí) - 稱為 session id,如果已包含一個(gè)session id則說明以前已經(jīng)為此客戶端創(chuàng)建過session,服務(wù)器就按照session id把這個(gè) session檢索出來使用(如果檢索不到,可能會(huì)新建一個(gè)),如果客戶端請(qǐng)求不包含session id,則為此客戶端創(chuàng)建一個(gè)session并且生成一個(gè)與此session相關(guān)聯(lián)的session id,session id的值應(yīng)該是一個(gè)既不會(huì)重復(fù),又不容易被找到規(guī)律以仿造的字符串,這個(gè) session id將被在本次響應(yīng)中返回給客戶端保存。

          保存這個(gè)session id的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動(dòng)的按照規(guī)則把這個(gè)標(biāo)識(shí)發(fā)揮給服務(wù)器。一般這個(gè)cookie的名字都是類似于SEEESIONID,而。比如weblogic對(duì)于web應(yīng)用程序生成的cookie,JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。

          由于cookie可以被人為的禁止,必須有其他機(jī)制以便在cookie被禁止時(shí)仍然能夠把session id傳遞回服務(wù)器。經(jīng)常被使用的一種技術(shù)叫做URL重寫,就是把session id直接附加在URL路徑的后面,附加方式也有兩種,一種是作為URL路徑的附加信息,表現(xiàn)形式為http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
          另一種是作為查詢字符串附加在URL后面,表現(xiàn)形式為http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
          這兩種方式對(duì)于用戶來說是沒有區(qū)別的,只是服務(wù)器在解析的時(shí)候處理的方式不同,采用第一種方式也有利于把session id的信息和正常程序參數(shù)區(qū)分開來。
          為了在整個(gè)交互過程中始終保持狀態(tài),就必須在每個(gè)客戶端可能請(qǐng)求的路徑后面都包含這個(gè)session id。

          另一種技術(shù)叫做表單隱藏字段。就是服務(wù)器會(huì)自動(dòng)修改表單,添加一個(gè)隱藏字段,以便在表單提交時(shí)能夠把session id傳遞回服務(wù)器。比如下面的表單
          <form name="testform" action="/xxx">
          <input type="text">
          </form>
          在被傳遞給客戶端之前將被改寫成
          <form name="testform" action="/xxx">
          <input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
          <input type="text">
          </form>
          這種技術(shù)現(xiàn)在已較少應(yīng)用,筆者接觸過的很古老的iPlanet6(SunONE應(yīng)用服務(wù)器的前身)就使用了這種技術(shù)。
          實(shí)際上這種技術(shù)可以簡(jiǎn)單的用對(duì)action應(yīng)用URL重寫來代替。

          在談?wù)搒ession機(jī)制的時(shí)候,常常聽到這樣一種誤解“只要關(guān)閉瀏覽器,session就消失了”。其實(shí)可以想象一下會(huì)員卡的例子,除非顧客主動(dòng)對(duì)店家提出銷卡,否則店家絕對(duì)不會(huì)輕易刪除顧客的資料。對(duì)session來說也是一樣的,除非程序通知服務(wù)器刪除一個(gè)session,否則服務(wù)器會(huì)一直保留,程序一般都是在用戶做log off的時(shí)候發(fā)個(gè)指令去刪除session。然而瀏覽器從來不會(huì)主動(dòng)在關(guān)閉之前通知服務(wù)器它將要關(guān)閉,因此服務(wù)器根本不會(huì)有機(jī)會(huì)知道瀏覽器已經(jīng)關(guān)閉,之所以會(huì)有這種錯(cuò)覺,是大部分session機(jī)制都使用會(huì)話cookie來保存session id,而關(guān)閉瀏覽器后這個(gè) session id就消失了,再次連接服務(wù)器時(shí)也就無法找到原來的session。如果服務(wù)器設(shè)置的cookie被保存到硬盤上,或者使用某種手段改寫瀏覽器發(fā)出的HTTP請(qǐng)求頭,把原來的session id發(fā)送給服務(wù)器,則再次打開瀏覽器仍然能夠找到原來的session。

          恰恰是由于關(guān)閉瀏覽器不會(huì)導(dǎo)致session被刪除,迫使服務(wù)器為seesion設(shè)置了一個(gè)失效時(shí)間,當(dāng)距離客戶端上一次使用session的時(shí)間超過這個(gè)失效時(shí)間時(shí),服務(wù)器就可以認(rèn)為客戶端已經(jīng)停止了活動(dòng),才會(huì)把session刪除以節(jié)省存儲(chǔ)空間。

          五、理解javax.servlet.http.HttpSession
          HttpSession是Java平臺(tái)對(duì)session機(jī)制的實(shí)現(xiàn)規(guī)范,因?yàn)樗鼉H僅是個(gè)接口,具體到每個(gè)web應(yīng)用服務(wù)器的提供商,除了對(duì)規(guī)范支持之外,仍然會(huì)有一些規(guī)范里沒有規(guī)定的細(xì)微差異。這里我們以BEA的Weblogic Server8.1作為例子來演示。

          首先,Weblogic Server提供了一系列的參數(shù)來控制它的HttpSession的實(shí)現(xiàn),包括使用cookie的開關(guān)選項(xiàng),使用URL重寫的開關(guān)選項(xiàng),session持久化的設(shè)置,session失效時(shí)間的設(shè)置,以及針對(duì)cookie的各種設(shè)置,比如設(shè)置cookie的名字、路徑、域, cookie的生存時(shí)間等。

          一般情況下,session都是存儲(chǔ)在內(nèi)存里,當(dāng)服務(wù)器進(jìn)程被停止或者重啟的時(shí)候,內(nèi)存里的session也會(huì)被清空,如果設(shè)置了session的持久化特性,服務(wù)器就會(huì)把session保存到硬盤上,當(dāng)服務(wù)器進(jìn)程重新啟動(dòng)或這些信息將能夠被再次使用, Weblogic Server支持的持久性方式包括文件、數(shù)據(jù)庫(kù)、客戶端cookie保存和復(fù)制。

          復(fù)制嚴(yán)格說來不算持久化保存,因?yàn)閟ession實(shí)際上還是保存在內(nèi)存里,不過同樣的信息被復(fù)制到各個(gè)cluster內(nèi)的服務(wù)器進(jìn)程中,這樣即使某個(gè)服務(wù)器進(jìn)程停止工作也仍然可以從其他進(jìn)程中取得session。

          cookie生存時(shí)間的設(shè)置則會(huì)影響瀏覽器生成的cookie是否是一個(gè)會(huì)話cookie。默認(rèn)是使用會(huì)話cookie。有興趣的可以用它來試驗(yàn)我們?cè)诘谒墓?jié)里提到的那個(gè)誤解。

          cookie的路徑對(duì)于web應(yīng)用程序來說是一個(gè)非常重要的選項(xiàng),Weblogic Server對(duì)這個(gè)選項(xiàng)的默認(rèn)處理方式使得它與其他服務(wù)器有明顯的區(qū)別。后面我們會(huì)專題討論。

          關(guān)于session的設(shè)置參考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

          六、HttpSession常見問題
          (在本小節(jié)中session的含義為⑤和⑥的混合)


          1、session在何時(shí)被創(chuàng)建
          一個(gè)常見的誤解是以為session在有客戶端訪問時(shí)就被創(chuàng)建,然而事實(shí)是直到某server端程序調(diào)用 HttpServletRequest.getSession(true)這樣的語(yǔ)句時(shí)才被創(chuàng)建,注意如果JSP沒有顯示的使用 <% @page session="false"%> 關(guān)閉session,則JSP文件在編譯成Servlet時(shí)將會(huì)自動(dòng)加上這樣一條語(yǔ)句 HttpSession session = HttpServletRequest.getSession(true);這也是JSP中隱含的 session對(duì)象的來歷。

          由于session會(huì)消耗內(nèi)存資源,因此,如果不打算使用session,應(yīng)該在所有的JSP中關(guān)閉它。

          2、session何時(shí)被刪除
          綜合前面的討論,session在下列情況下被刪除a.程序調(diào)用HttpSession.invalidate();或b.距離上一次收到客戶端發(fā)送的session id時(shí)間間隔超過了session的超時(shí)設(shè)置;或c.服務(wù)器進(jìn)程被停止(非持久session)

          3、如何做到在瀏覽器關(guān)閉時(shí)刪除session
          嚴(yán)格的講,做不到這一點(diǎn)??梢宰鲆稽c(diǎn)努力的辦法是在所有的客戶端頁(yè)面里使用javascript代碼window.oncolose來監(jiān)視瀏覽器的關(guān)閉動(dòng)作,然后向服務(wù)器發(fā)送一個(gè)請(qǐng)求來刪除session。但是對(duì)于瀏覽器崩潰或者強(qiáng)行殺死進(jìn)程這些非常規(guī)手段仍然無能為力。

          4、有個(gè)HttpSessionListener是怎么回事
          你可以創(chuàng)建這樣的listener去監(jiān)控session的創(chuàng)建和銷毀事件,使得在發(fā)生這樣的事件時(shí)你可以做一些相應(yīng)的工作。注意是session的創(chuàng)建和銷毀動(dòng)作觸發(fā)listener,而不是相反。類似的與HttpSession有關(guān)的listener還有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。

          5、存放在session中的對(duì)象必須是可序列化的嗎
          不是必需的。要求對(duì)象可序列化只是為了session能夠在集群中被復(fù)制或者能夠持久保存或者在必要時(shí)server能夠暫時(shí)把session交換出內(nèi)存。在 Weblogic Server的session中放置一個(gè)不可序列化的對(duì)象在控制臺(tái)上會(huì)收到一個(gè)警告。我所用過的某個(gè)iPlanet版本如果 session中有不可序列化的對(duì)象,在session銷毀時(shí)會(huì)有一個(gè)Exception,很奇怪。

          6、如何才能正確的應(yīng)付客戶端禁止cookie的可能性
          對(duì)所有的URL使用URL重寫,包括超鏈接,form的action,和重定向的URL,具體做法參見[6]
          http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

          7、開兩個(gè)瀏覽器窗口訪問應(yīng)用程序會(huì)使用同一個(gè)session還是不同的session
          參見第三小節(jié)對(duì)cookie的討論,對(duì)session來說是只認(rèn)id不認(rèn)人,因此不同的瀏覽器,不同的窗口打開方式以及不同的cookie存儲(chǔ)方式都會(huì)對(duì)這個(gè)問題的答案有影響。

          8、如何防止用戶打開兩個(gè)瀏覽器窗口操作導(dǎo)致的session混亂
          這個(gè)問題與防止表單多次提交是類似的,可以通過設(shè)置客戶端的令牌來解決。就是在服務(wù)器每次生成一個(gè)不同的id返回給客戶端,同時(shí)保存在session里,客戶端提交表單時(shí)必須把這個(gè)id也返回服務(wù)器,程序首先比較返回的id與保存在session里的值是否一致,如果不一致則說明本次操作已經(jīng)被提交過了。可以參看《J2EE核心模式》關(guān)于表示層模式的部分。需要注意的是對(duì)于使用javascript window.open打開的窗口,一般不設(shè)置這個(gè)id,或者使用單獨(dú)的id,以防主窗口無法操作,建議不要再window.open打開的窗口里做修改操作,這樣就可以不用設(shè)置。

          9、為什么在Weblogic Server中改變session的值后要重新調(diào)用一次session.setValue
          做這個(gè)動(dòng)作主要是為了在集群環(huán)境中提示W(wǎng)eblogic Server session中的值發(fā)生了改變,需要向其他服務(wù)器進(jìn)程復(fù)制新的session值。

          10、為什么session不見了
          排除session正常失效的因素之外,服務(wù)器本身的可能性應(yīng)該是微乎其微的,雖然筆者在iPlanet6SP1加若干補(bǔ)丁的Solaris版本上倒也遇到過;瀏覽器插件的可能性次之,筆者也遇到過3721插件造成的問題;理論上防火墻或者代理服務(wù)器在cookie處理上也有可能會(huì)出現(xiàn)問題。
          出現(xiàn)這一問題的大部分原因都是程序的錯(cuò)誤,最常見的就是在一個(gè)應(yīng)用程序中去訪問另外一個(gè)應(yīng)用程序。我們?cè)谙乱还?jié)討論這個(gè)問題。

          七、跨應(yīng)用程序的session共享

          常常有這樣的情況,一個(gè)大項(xiàng)目被分割成若干小項(xiàng)目開發(fā),為了能夠互不干擾,要求每個(gè)小項(xiàng)目作為一個(gè)單獨(dú)的web應(yīng)用程序開發(fā),可是到了最后突然發(fā)現(xiàn)某幾個(gè)小項(xiàng)目之間需要共享一些信息,或者想使用session來實(shí)現(xiàn)SSO(single sign on),在session中保存login的用戶信息,最自然的要求是應(yīng)用程序間能夠訪問彼此的session。

          然而按照Servlet規(guī)范,session的作用范圍應(yīng)該僅僅限于當(dāng)前應(yīng)用程序下,不同的應(yīng)用程序之間是不能夠互相訪問對(duì)方的session的。各個(gè)應(yīng)用服務(wù)器從實(shí)際效果上都遵守了這一規(guī)范,但是實(shí)現(xiàn)的細(xì)節(jié)卻可能各有不同,因此解決跨應(yīng)用程序session共享的方法也各不相同。

          首先來看一下Tomcat是如何實(shí)現(xiàn)web應(yīng)用程序之間session的隔離的,從 Tomcat設(shè)置的cookie路徑來看,它對(duì)不同的應(yīng)用程序設(shè)置的cookie路徑是不同的,這樣不同的應(yīng)用程序所用的session id是不同的,因此即使在同一個(gè)瀏覽器窗口里訪問不同的應(yīng)用程序,發(fā)送給服務(wù)器的session id也可以是不同的。


            

          根據(jù)這個(gè)特性,我們可以推測(cè)Tomcat中session的內(nèi)存結(jié)構(gòu)大致如下。


           

          筆者以前用過的iPlanet也采用的是同樣的方式,估計(jì)SunONE與iPlanet之間不會(huì)有太大的差別。對(duì)于這種方式的服務(wù)器,解決的思路很簡(jiǎn)單,實(shí)際實(shí)行起來也不難。要么讓所有的應(yīng)用程序共享一個(gè)session id,要么讓應(yīng)用程序能夠獲得其他應(yīng)用程序的session id。

          iPlanet中有一種很簡(jiǎn)單的方法來實(shí)現(xiàn)共享一個(gè)session id,那就是把各個(gè)應(yīng)用程序的cookie路徑都設(shè)為/(實(shí)際上應(yīng)該是/NASApp,對(duì)于應(yīng)用程序來講它的作用相當(dāng)于根)。
          <session-info>
          <path>/NASApp</path>
          </session-info>

          需要注意的是,操作共享的session應(yīng)該遵循一些編程約定,比如在session attribute名字的前面加上應(yīng)用程序的前綴,使得 setAttribute("name", "neo")變成setAttribute("app1.name", "neo"),以防止命名空間沖突,導(dǎo)致互相覆蓋。


          在Tomcat中則沒有這么方便的選擇。在Tomcat版本3上,我們還可以有一些手段來共享session。對(duì)于版本4以上的Tomcat,目前筆者尚未發(fā)現(xiàn)簡(jiǎn)單的辦法。只能借助于第三方的力量,比如使用文件、數(shù)據(jù)庫(kù)、JMS或者客戶端cookie,URL參數(shù)或者隱藏字段等手段。

          我們?cè)倏匆幌耊eblogic Server是如何處理session的。


            

          從截屏畫面上可以看到Weblogic Server對(duì)所有的應(yīng)用程序設(shè)置的cookie的路徑都是/,這是不是意味著在Weblogic Server中默認(rèn)的就可以共享session了呢?然而一個(gè)小實(shí)驗(yàn)即可證明即使不同的應(yīng)用程序使用的是同一個(gè)session,各個(gè)應(yīng)用程序仍然只能訪問自己所設(shè)置的那些屬性。這說明Weblogic Server中的session的內(nèi)存結(jié)構(gòu)可能如下


           

          對(duì)于這樣一種結(jié)構(gòu),在 session機(jī)制本身上來解決session共享的問題應(yīng)該是不可能的了。除了借助于第三方的力量,比如使用文件、數(shù)據(jù)庫(kù)、JMS或者客戶端 cookie,URL參數(shù)或者隱藏字段等手段,還有一種較為方便的做法,就是把一個(gè)應(yīng)用程序的session放到ServletContext中,這樣另外一個(gè)應(yīng)用程序就可以從ServletContext中取得前一個(gè)應(yīng)用程序的引用。示例代碼如下,

          應(yīng)用程序A
          context.setAttribute("appA", session); 

          應(yīng)用程序B
          contextA = context.getContext("/appA");
          HttpSession sessionA = (HttpSession)contextA.getAttribute("appA"); 

          值得注意的是這種用法不可移植,因?yàn)楦鶕?jù)ServletContext的JavaDoc,應(yīng)用服務(wù)器可以處于安全的原因?qū)τ赾ontext.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通過。

          那么Weblogic Server為什么要把所有的應(yīng)用程序的cookie路徑都設(shè)為/呢?原來是為了SSO,凡是共享這個(gè)session的應(yīng)用程序都可以共享認(rèn)證的信息。一個(gè)簡(jiǎn)單的實(shí)驗(yàn)就可以證明這一點(diǎn),修改首先登錄的那個(gè)應(yīng)用程序的描述符weblogic.xml,把cookie路徑修改為/appA 訪問另外一個(gè)應(yīng)用程序會(huì)重新要求登錄,即使是反過來,先訪問cookie路徑為/的應(yīng)用程序,再訪問修改過路徑的這個(gè),雖然不再提示登錄,但是登錄的用戶信息也會(huì)丟失。注意做這個(gè)實(shí)驗(yàn)時(shí)認(rèn)證方式應(yīng)該使用FORM,因?yàn)闉g覽器和web服務(wù)器對(duì)basic認(rèn)證方式有其他的處理方式,第二次請(qǐng)求的認(rèn)證不是通過 session來實(shí)現(xiàn)的。具體請(qǐng)參看[7] secion 14.8 Authorization,你可以修改所附的示例程序來做這些試驗(yàn)。

          八、總結(jié)
          session機(jī)制本身并不復(fù)雜,然而其實(shí)現(xiàn)和配置上的靈活性卻使得具體情況復(fù)雜多變。這也要求我們不能把僅僅某一次的經(jīng)驗(yàn)或者某一個(gè)瀏覽器,服務(wù)器的經(jīng)驗(yàn)當(dāng)作普遍適用的經(jīng)驗(yàn),而是始終需要具體情況具體分析。

          關(guān)于作者:
          郎云鵬(dev2dev ID: hippiewolf),軟件工程師,從事J2EE開發(fā)
          電子郵件:langyunpeng@yahoo.com.cn
          地址:大連軟件園路31號(hào)科技大廈A座大連博涵咨詢服務(wù)有限公司

          參考文檔:
          [1] Preliminary Specification http://wp.netscape.com/newsref/std/cookie_spec.html
          [2] RFC2109 http://www.rfc-editor.org/rfc/rfc2109.txt
          [3] RFC2965 http://www.rfc-editor.org/rfc/rfc2965.txt
          [4] The Unofficial Cookie FAQ http://www.cookiecentral.com/faq/
          [5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
          [6] http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
          [7] RFC2616 http://www.rfc-editor.org/rfc/rfc2616.txt

          posted @ 2005-09-06 21:37 春雷的博客 閱讀(567) | 評(píng)論 (0)編輯 收藏

          主站蜘蛛池模板: 钦州市| 红安县| 西峡县| 收藏| 贵德县| 韩城市| 读书| 峡江县| 东方市| 夹江县| 佛学| 南和县| 安福县| 永春县| 潞西市| 安龙县| 肇庆市| 梓潼县| 武鸣县| 东山县| 根河市| 崇信县| 两当县| 嘉祥县| 鱼台县| 石家庄市| 丰台区| 宁明县| 杭锦后旗| 洛扎县| 卢湾区| 巴塘县| 靖州| 金华市| 大邑县| 琼中| 兴和县| 方山县| 南陵县| 齐齐哈尔市| 门头沟区|