隨筆-314  評(píng)論-209  文章-0  trackbacks-0
          Java語(yǔ)法總結(jié) - 線程

          一提到線程好像是件很麻煩很復(fù)雜的事,事實(shí)上確實(shí)如此,涉及到線程的編程是很講究技巧的。這就需要我們變換思維方式,了解線程機(jī)制的比較通用的技巧,寫出高效的、不依賴于某個(gè)JVM實(shí)現(xiàn)的程序來(lái)。畢竟僅僅就Java而言,各個(gè)虛擬機(jī)的實(shí)現(xiàn)是不同的。學(xué)習(xí)線程時(shí),最令我印象深刻的就是那種不確定性、沒有保障性,各個(gè)線程的運(yùn)行完全是以不可預(yù)料的方式和速度推進(jìn),有的一個(gè)程序運(yùn)行了N次,其結(jié)果差異性很大。


          1、什么是線程?線程是彼此互相獨(dú)立的、能獨(dú)立運(yùn)行的子任務(wù),并且每個(gè)線程都有自己的調(diào)用棧。所謂的多任務(wù)是通過周期性地將CPU時(shí)間片切換到不同的子任務(wù),雖然從微觀上看來(lái),單核的CPU上同時(shí)只運(yùn)行一個(gè)子任務(wù),但是從宏觀來(lái)看,每個(gè)子任務(wù)似乎是同時(shí)連續(xù)運(yùn)行的。(但是JAVA的線程不是按時(shí)間片分配的,在本文的最后引用了一段網(wǎng)友翻譯的JAVA原著中對(duì)線程的理解。)

          2、在java中,線程指兩個(gè)不同的內(nèi)容:一是java.lang.Thread類的一個(gè)對(duì)象;另外也可以指線程的執(zhí)行。線程對(duì)象和其他的對(duì)象一樣,在堆上創(chuàng)建、運(yùn)行、死亡。但不同之處是線程的執(zhí)行是一個(gè)輕量級(jí)的進(jìn)程,有它自己的調(diào)用棧。
          可以這樣想,每個(gè)調(diào)用棧都對(duì)應(yīng)一個(gè)線程,每個(gè)線程又對(duì)應(yīng)一個(gè)調(diào)用棧。
          我們運(yùn)行java程序時(shí)有一個(gè)入口函數(shù)main()函數(shù),它對(duì)應(yīng)的線程被稱為主線程。一個(gè)新線程一旦被創(chuàng)建,就產(chǎn)生一個(gè)新調(diào)用棧,從原主線程中脫離,也就是與主線程并發(fā)執(zhí)行。


          4、當(dāng)提到線程時(shí),很少是有保障的。我們必須了解到什么是有保障的操作,什么是無(wú)保障的操作,以便設(shè)計(jì)的程序在各種jvm上都能很好地工作。比如,在某些jvm實(shí)現(xiàn)中,把java線程映射為本地操作系統(tǒng)的線程。這是java核心的一部分。

          5、線程的創(chuàng)建。
          創(chuàng)建線程有兩種方式:
          A、繼承java.lang.Thread類。
              class ThreadTest extends Thread{
                  public void run() {
                      System.out.println ("someting run here!");
                  }
                  public void run(String s){
                      System.out.println ("string in run is " + s);
                  }
                  public static void main (String[] args) {
                      ThreadTest tt = new ThreadTest();
                      tt.start();
                      tt.run("it won't auto run!");
                  }
              }

          輸出的結(jié)果比較有趣:
          string in run is it won't auto run!
          someting run here!
          注意輸出的順序:好像與我們想象的順序相反了!為什么呢?
          一旦調(diào)用start()方法,必須給JVM點(diǎn)時(shí)間,讓它配置進(jìn)程。而在它配置完成之前,重載的run(String s)方法被調(diào)用了,結(jié)果反而先輸出了“string in run is it won't auto run!”,這時(shí)tt線程完成了配置,輸出了“someting run here!”。
          這個(gè)結(jié)論是比較容易驗(yàn)證的:
          修改上面的程序,在tt.start();后面加上語(yǔ)句for (int i = 0; i<10000; i++); 這樣主線程開始執(zhí)行運(yùn)算量比較大的for循環(huán)了,只有執(zhí)行完for循環(huán)才能運(yùn)行后面的tt.run("it won't auto run!");語(yǔ)句。此時(shí),tt線程和主線程并行執(zhí)行了,已經(jīng)有足夠的時(shí)間完成線程的配置!因此先到一步!修改后的程序運(yùn)行結(jié)果如下:
          someting run here!
          string in run is it won't auto run!
          注意:這種輸出結(jié)果的順序是沒有保障的!不要依賴這種結(jié)論!

          沒有參數(shù)的run()方法是自動(dòng)被調(diào)用的,而帶參數(shù)的run()是被重載的,必須顯式調(diào)用。
          這種方式的限制是:這種方式很簡(jiǎn)單,但不是個(gè)好的方案。如果繼承了Thread類,那么就不能繼承其他的類了,java是單繼承結(jié)構(gòu)的,應(yīng)該把繼承的機(jī)會(huì)留給別的類。除非因?yàn)槟阌芯€程特有的更多的操作。
          Thread類中有許多管理線程的方法,包括創(chuàng)建、啟動(dòng)和暫停它們。所有的操作都是從run()方法開始,并且在run()方法內(nèi)編寫需要在獨(dú)立線程內(nèi)執(zhí)行的代碼。run()方法可以調(diào)用其他方法,但是執(zhí)行的線程總是通過調(diào)用run()。

          B、實(shí)現(xiàn)java.lang.Runnable接口。
              class ThreadTest implements Runnable {
                  public void run() {
                      System.out.println ("someting run here");
                  }
                  public static void main (String[] args) {
                      ThreadTest tt = new ThreadTest();
                  Thread t1 = new Thread(tt);
                  Thread t2 = new Thread(tt);
                  t1.start();
                  t2.start();
                      //new Thread(tt).start();
                  }
              }

          比第一種方法復(fù)雜一點(diǎn),為了使代碼被獨(dú)立的線程運(yùn)行,還需要一個(gè)Thread對(duì)象。這樣就把線程相關(guān)的代碼和線程要執(zhí)行的代碼分離開來(lái)。

          另一種方式是:參數(shù)形式的匿名內(nèi)部類創(chuàng)建方式,也是比較常見的。
              class ThreadTest{
                  public static void main (String[] args) {
                      Thread t = new Thread(new Runnable(){
                          public void run(){
                              System.out.println ("anonymous thread");
                          }
                      });    
                      
                      t.start();
                  }
              }
          如果你對(duì)此方式的聲明不感冒,請(qǐng)參看本人總結(jié)的內(nèi)部類。

          第一種方式使用無(wú)參構(gòu)造函數(shù)創(chuàng)建線程,則當(dāng)線程開始工作時(shí),它將調(diào)用自己的run()方法。
          第二種方式使用帶參數(shù)的構(gòu)造函數(shù)創(chuàng)建線程,因?yàn)槟阋嬖V這個(gè)新線程使用你的run()方法,而不是它自己的。
          如上例,可以把一個(gè)目標(biāo)賦給多個(gè)線程,這意味著幾個(gè)執(zhí)行線程將運(yùn)行完全相同的作業(yè)。

          6、什么時(shí)候線程是活的?
          在調(diào)用start()方法開始執(zhí)行線程之前,線程的狀態(tài)還不是活的。測(cè)試程序如下:
              class ThreadTest implements Runnable {
                  public void run() {
                      System.out.println ("someting run here");
                  }
                  public static void main (String[] args) {
                      ThreadTest tt = new ThreadTest();
                      Thread t1 = new Thread(tt);
                      System.out.println (t1.isAlive());
                      t1.start();
                      System.out.println (t1.isAlive());
                  }
              }

          結(jié)果輸出:
          false
          true
          isAlive方法是確定一個(gè)線程是否已經(jīng)啟動(dòng),而且還沒完成run()方法內(nèi)代碼的最好方法。

          7、啟動(dòng)新線程。
          線程的啟動(dòng)要調(diào)用start()方法,只有這樣才能創(chuàng)建新的調(diào)用棧。而直接調(diào)用run()方法的話,就不會(huì)創(chuàng)建新的調(diào)用棧,也就不會(huì)創(chuàng)建新的線程,run()方法就與普通的方法沒什么兩樣了!

          8、給線程起個(gè)有意義的名字。
          沒有該線程命名的話,線程會(huì)有一個(gè)默認(rèn)的名字,格式是:“Thread-”加上線程的序號(hào),如:Thread-0
          這看起來(lái)可讀性不好,不能從名字分辨出該線程具有什么功能。下面是給線程命名的方式。
          第一種:用setName()函數(shù)
          第二種:選用帶線程命名的構(gòu)造器
              class ThreadTest implements Runnable{
                  public void run(){
                      System.out.println (Thread.currentThread().getName());
                  }
                  public static void main (String[] args) {
                  ThreadTest tt = new ThreadTest();     
                  //Thread t = new Thread (tt,"eat apple");
                  Thread t = new Thread (tt);
                  t.setName("eat apple");
                  t.start();
                  }
              }

          9、“沒有保障”的多線程的運(yùn)行。下面的代碼可能令人印象深刻。
              class ThreadTest implements Runnable{
                  public void run(){
                      System.out.println (Thread.currentThread().getName());
                  }
                  public static void main (String[] args) {
                      ThreadTest tt = new ThreadTest();
                      Thread[] ts =new Thread[10];
                  
                      for (int i =0; i < ts.length; i++)
                          ts[i] = new Thread(tt);
                          
                      for (Thread t : ts)
                          t.start();
                  }
              }
          在我的電腦上運(yùn)行的結(jié)果是:
          Thread-0
          Thread-1
          Thread-3
          Thread-5
          Thread-2
          Thread-7
          Thread-4
          Thread-9
          Thread-6
          Thread-8
          而且每次運(yùn)行的結(jié)果都是不同的!繼續(xù)引用前面的話,一旦涉及到線程,其運(yùn)行多半是沒有保障。這個(gè)保障是指線程的運(yùn)行完全是由調(diào)度程序控制的,我們沒法控制它的執(zhí)行順序,持續(xù)時(shí)間也沒有保障,有著不可預(yù)料的結(jié)果。


          10、線程的狀態(tài)。
          A、新狀態(tài)。
          實(shí)例化Thread對(duì)象,但沒有調(diào)用start()方法時(shí)的狀態(tài)。
          ThreadTest tt = new ThreadTest();     
          或者Thread t = new Thread (tt);
          此時(shí)雖然創(chuàng)建了Thread對(duì)象,如前所述,但是它們不是活的,不能通過isAlive()測(cè)試。

          B、就緒狀態(tài)。
          線程有資格運(yùn)行,但調(diào)度程序還沒有把它選為運(yùn)行線程所處的狀態(tài)。也就是具備了運(yùn)行的條件,一旦被選中馬上就能運(yùn)行。
          也是調(diào)用start()方法后但沒運(yùn)行的狀態(tài)。此時(shí)雖然沒在運(yùn)行,但是被認(rèn)為是活的,能通過isAlive()測(cè)試。而且在線程運(yùn)行之后、或者被阻塞、等待或者睡眠狀態(tài)回來(lái)之后,線程首先進(jìn)入就緒狀態(tài)。

          C、運(yùn)行狀態(tài)。
          從就緒狀態(tài)池(注意不是隊(duì)列,是池)中選擇一個(gè)為當(dāng)前執(zhí)行進(jìn)程時(shí),該線程所處的狀態(tài)。

          D、等待、阻塞、睡眠狀態(tài)。
          這三種狀態(tài)有一個(gè)共同點(diǎn):線程依然是活的,但是缺少運(yùn)行的條件,一旦具備了條就就可以轉(zhuǎn)為就緒狀態(tài)(不能直接轉(zhuǎn)為運(yùn)行狀態(tài))。另外,suspend()和stop()方法已經(jīng)被廢棄了,比較危險(xiǎn),不要再用了。

          E、死亡狀態(tài)。
          一個(gè)線程的run()方法運(yùn)行結(jié)束,那么該線程完成其歷史使命,它的棧結(jié)構(gòu)將解散,也就是死亡了。但是它仍然是一個(gè)Thread對(duì)象,我們?nèi)钥梢砸盟拖衿渌麑?duì)象一樣!它也不會(huì)被垃圾回收器回收了,因?yàn)閷?duì)該對(duì)象的引用仍然存在。
          如此說(shuō)來(lái),即使run()方法運(yùn)行結(jié)束線程也沒有死啊!事實(shí)是,一旦線程死去,它就永遠(yuǎn)不能重新啟動(dòng)了,也就是說(shuō),不能再用start()方法讓它運(yùn)行起來(lái)!如果強(qiáng)來(lái)的話會(huì)拋出IllegalThreadStateException異常。如:
          t.start();
          t.start();
          放棄吧,人工呼吸或者心臟起搏器都無(wú)濟(jì)于事……線程也屬于一次性用品。

          11、阻止線程運(yùn)行。
          A、睡眠。sleep()方法
          讓線程睡眠的理由很多,比如:認(rèn)為該線程運(yùn)行得太快,需要減緩一下,以便和其他線程協(xié)調(diào);查詢當(dāng)時(shí)的股票價(jià)格,每睡5分鐘查詢一次,可以節(jié)省帶寬,而且即時(shí)性要求也不那么高。
          用Thread的靜態(tài)方法可以實(shí)現(xiàn)Thread.sleep(5*60*1000); 睡上5分鐘吧。sleep的參數(shù)是毫秒。但是要注意sleep()方法會(huì)拋出檢查異常InterruptedException,對(duì)于檢查異常,我們要么聲明,要么使用處理程序。
              try {
                  Thread.sleep(20000);
              }
              catch (InterruptedException ie) {
                  ie.printStackTrace();
              }
          既然有了sleep()方法,我們是不是可以控制線程的執(zhí)行順序了!每個(gè)線程執(zhí)行完畢都睡上一覺?這樣就能控制線程的運(yùn)行順序了,下面是書上的一個(gè)例子:
              class ThreadTest implements Runnable{
                  public void run(){
                      for (int i = 1; i<4; i++){
                          System.out.println (Thread.currentThread().getName());
                          try {
                              Thread.sleep(1000);
                          } catch (InterruptedException ie) { }
                      }
                  }
                  public static void main (String[] args) {
                      ThreadTest tt = new ThreadTest();
                      Thread t0 = new Thread(tt,"Thread 0");
                      Thread t1 = new Thread(tt,"Thread 1");
                      Thread t2 = new Thread(tt,"Thread 2");
                      t0.start();
                      t1.start();
                      t2.start();            
                  }
              }

          并且給出了結(jié)果:
          Thread 0
          Thread 1
          Thread 2
          Thread 0
          Thread 1
          Thread 2
          Thread 0
          Thread 1
          Thread 2
          也就是Thread 0  Thread 1 Thread 2 按照這個(gè)順序交替出現(xiàn),作者指出雖然結(jié)果和我們預(yù)料的似乎相同,但是這個(gè)結(jié)果是不可靠的。果然被我的雙核電腦驗(yàn)證了:
          Thread 0
          Thread 1
          Thread 2
          Thread 2
          Thread 0
          Thread 1
          Thread 1
          Thread 0
          Thread 2
          看來(lái)線程真的很不可靠啊。但是盡管如此,sleep()方法仍然是保證所有線程都有運(yùn)行機(jī)會(huì)的最好方法。至少它保證了一個(gè)線程進(jìn)入運(yùn)行之后不會(huì)一直到運(yùn)行完位置。

          時(shí)間的精確性。再?gòu)?qiáng)調(diào)一下,線程醒來(lái)之后不會(huì)進(jìn)入運(yùn)行狀態(tài),而是進(jìn)入就緒狀態(tài)。因此sleep()中指定的時(shí)間不是線程不運(yùn)行的精確時(shí)間!不能依賴sleep()方法提供十分精確的定時(shí)。我們可以看到很多應(yīng)用程序用sleep()作為定時(shí)器,而且沒什么不好的,確實(shí)如此,但是我們一定要知道sleep()不能保證線程醒來(lái)就能馬上進(jìn)入運(yùn)行狀態(tài),是不精確的。

          sleep()方法是一個(gè)靜態(tài)的方法,它所指的是當(dāng)前正在執(zhí)行的線程休眠一個(gè)毫秒數(shù)。看到某些書上的Thread.currentThread().sleep(1000); ,其實(shí)是不必要的。Thread.sleep(1000);就可以了。類似于getName()方法不是靜態(tài)方法,它必須針對(duì)具體某個(gè)線程對(duì)象,這時(shí)用取得當(dāng)前線程的方法Thread.currentThread().getName();

          B、線程優(yōu)先級(jí)和讓步。
          線程的優(yōu)先級(jí)。在大多數(shù)jvm實(shí)現(xiàn)中調(diào)度程序使用基于線程優(yōu)先級(jí)的搶先調(diào)度機(jī)制。如果一個(gè)線程進(jìn)入可運(yùn)行狀態(tài),并且它比池中的任何其他線程和當(dāng)前運(yùn)行的進(jìn)程的具有更高的優(yōu)先級(jí),則優(yōu)先級(jí)較低的線程進(jìn)入可運(yùn)行狀態(tài),最高優(yōu)先級(jí)的線程被選擇去執(zhí)行。

          于是就有了這樣的結(jié)論:當(dāng)前運(yùn)行線程的優(yōu)先級(jí)通常不會(huì)比池中任何線程的優(yōu)先級(jí)低。但是并不是所有的jvm的調(diào)度都這樣,因此一定不能依賴于線程優(yōu)先級(jí)來(lái)保證程序的正確操作,這仍然是沒有保障的,要把線程優(yōu)先級(jí)用作一種提高程序效率的方法,并且這種方法也不能依賴優(yōu)先級(jí)的操作。

          另外一個(gè)沒有保障的操作是:當(dāng)前運(yùn)行的線程與池中的線程,或者池中的線程具有相同的優(yōu)先級(jí)時(shí),JVM的調(diào)度實(shí)現(xiàn)會(huì)選擇它喜歡的線程。也許是選擇一個(gè)去運(yùn)行,直至其完成;或者用分配時(shí)間片的方式,為每個(gè)線程提供均等的機(jī)會(huì)。

          優(yōu)先級(jí)用正整數(shù)設(shè)置,通常為1-10,JVM從不會(huì)改變一個(gè)線程的優(yōu)先級(jí)。默認(rèn)情況下,優(yōu)先級(jí)是5。Thread類具有三個(gè)定義線程優(yōu)先級(jí)范圍的靜態(tài)最終常量:Thread.MIN_PRIORITY (為1) Thread.NORM_PRIORITY (為5) Thread.MAX_PRIORITY (為10)

          靜態(tài)Thread.yield()方法。
          它的作用是讓當(dāng)前運(yùn)行的線程回到可運(yùn)行狀態(tài),以便讓具有同等優(yōu)先級(jí)的其他線程運(yùn)行。用yield()方法的目的是讓同等優(yōu)先級(jí)的線程能適當(dāng)?shù)剌嗈D(zhuǎn)。但是,并不能保證達(dá)到此效果!因?yàn)椋词巩?dāng)前變成可運(yùn)行狀態(tài),可是還有可能再次被JVM選中!也就是連任。

          非靜態(tài)join()方法。
          讓一個(gè)線程加入到另一個(gè)線程的尾部。讓B線程加入A線程,意味著在A線程運(yùn)行完成之前,B線程不會(huì)進(jìn)入可運(yùn)行狀態(tài)。
              Thread t = new Thread();
              t.start();
              t.join;
          這段代碼的意思是取得當(dāng)前的線程,把它加入到t線程的尾部,等t線程運(yùn)行完畢之后,原線程繼續(xù)運(yùn)行。書中的例子在我的電腦里效果很糟糕,看不出什么效果來(lái)。也許是CPU太快了,而且是雙核的;也許是JDK1.6的原因?

          12、沒總結(jié)完。線程這部分很重要,內(nèi)容也很多,看太快容易消化不良,偶要慢慢地消化掉……



          附: java原著中對(duì)線程的解釋。

          e文原文:

          Thread Scheduling

          In Java technology,threads are usually preemptive,but not necessarily Time-sliced(the process of giving each thread an equal amount of CPU time).It is common mistake to believe that "preemptive" is a fancy word for "does time-slicing".

          For the runtime on a Solaris Operating Environment platform,Java technology does not preempt threads of the same priority.However,the runtime on Microsoft Windows platforms uses time-slicing,so it preempts threads of the same priority and even threads of higher priority.Preemption is not guaranteed;however,most JVM implementations result in behavior that appears to be strictly preemptive.Across JVM implementations,there is no absolute guarantee of preemption or time-slicing.The only guarantees lie in the coder’s use of wait and sleep.

          The model of a preemptive scheduler is that many threads might be runnable,but only one thread is actually running.This thread continues to run until it ceases to be runnable or another thread of higher priority becomes runnable.In the latter case,the lower priority thread is preempted by the thread of higher priority,which gets a chance to run instead.

          A thread might cease to runnable (that is,because blocked) for a variety of reasons.The thread’s code can execute a Thread.sleep() call,deliberately asking the thread to pause for a fixed period of time.The thread might have to wait to access a resource and cannot continue until that resource become available.

          All thread that are runnable are kept in pools according to priority.When a blocked thread becomes runnable,it is placed back into the appropriate runnable pool.Threads from the highest priority nonempty pool are given CPU time.

          The last sentence is worded loosed because:
          (1) In most JVM implementations,priorities seem to work in a preemptive manner,although there is no guarantee that priorities have any meaning at all;
          (2) Microsoft Window’s values affect thread behavior so that it is possible that a Java Priority 4 thread might be running,in spite of the fact that a runnable Java Priority 5 thread is waiting for the CPU.
          In reality,many JVMs implement pool as queues,but this is not guaranteed hehavior.


          熱心網(wǎng)友翻譯的版本:

          在java技術(shù)中,線程通常是搶占式的而不需要時(shí)間片分配進(jìn)程(分配給每個(gè)線程相等的cpu時(shí)間的進(jìn)程)。一個(gè)經(jīng)常犯的錯(cuò)誤是認(rèn)為“搶占”就是“分配時(shí)間片”。
          在Solaris平臺(tái)上的運(yùn)行環(huán)境中,相同優(yōu)先級(jí)的線程不能相互搶占對(duì)方的cpu時(shí)間。但是,在使用時(shí)間片的windows平臺(tái)運(yùn)行環(huán)境中,可以搶占相同甚至更高優(yōu)先級(jí)的線程的cpu時(shí)間。搶占并不是絕對(duì)的,可是大多數(shù)的JVM的實(shí)現(xiàn)結(jié)果在行為上表現(xiàn)出了嚴(yán)格的搶占。縱觀JVM的實(shí)現(xiàn),并沒有絕對(duì)的搶占或是時(shí)間片,而是依賴于編碼者對(duì)wait和sleep這兩個(gè)方法的使用。
          搶占式調(diào)度模型就是許多線程屬于可以運(yùn)行狀態(tài)(等待狀態(tài)),但實(shí)際上只有一個(gè)線程在運(yùn)行。該線程一直運(yùn)行到它終止進(jìn)入可運(yùn)行狀態(tài)(等待狀態(tài))或是另一個(gè)具有更高優(yōu)先級(jí)的線程變成可運(yùn)行狀態(tài)。在后一種情況下,底優(yōu)先級(jí)的線程被高優(yōu)先級(jí)的線程搶占,高優(yōu)先級(jí)的線程獲得運(yùn)行的機(jī)會(huì)。
          線程可以因?yàn)楦鞣N各樣的原因終止并進(jìn)入可運(yùn)行狀態(tài)(因?yàn)槎氯@纾€程的代碼可以在適當(dāng)時(shí)候執(zhí)行Thread.sleep()方法,故意讓線程中止;線程可能為了訪問資源而不得不等待直到該資源可用為止。
          所有可運(yùn)行的線程根據(jù)優(yōu)先級(jí)保持在不同的池中。一旦被堵塞的線程進(jìn)入可運(yùn)行狀態(tài),它將會(huì)被放回適當(dāng)?shù)目蛇\(yùn)行池中。非空最高優(yōu)先級(jí)的池中的線程將獲得cpu時(shí)間。
          最后一個(gè)句子是不精確的,因?yàn)椋?br /> (1)在大多數(shù)的JVM實(shí)現(xiàn)中,雖然不能保證說(shuō)優(yōu)先級(jí)有任何意義,但優(yōu)先級(jí)看起來(lái)象是用搶占方式工作。
          (2)微軟windows的評(píng)價(jià)影響線程的行為,以至盡管一個(gè)處于可運(yùn)行狀態(tài)的優(yōu)先級(jí)為5的java線程正在等待cpu時(shí)間,但是一個(gè)優(yōu)先級(jí)為4的java線程卻可能正在運(yùn)行。
          實(shí)際上,許多JVM用隊(duì)列來(lái)實(shí)現(xiàn)池,但沒有保證行為。
          posted on 2008-11-21 18:01 xzc 閱讀(212) 評(píng)論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 广灵县| 长子县| 临泉县| 图木舒克市| 开江县| 普宁市| 萝北县| 克什克腾旗| 丽江市| 依安县| 阳信县| 河曲县| 东平县| 大新县| 彭山县| 特克斯县| 红桥区| 南和县| 河东区| 兴安盟| 恭城| 海原县| 奉节县| 南澳县| 四平市| 黔西| 犍为县| 常山县| 清镇市| 清流县| 宁蒗| 平阳县| 呈贡县| 沛县| 谷城县| 健康| 常熟市| 方城县| 永善县| 碌曲县| 化德县|