yeshucheng
          追逐自己,追逐方向,心隨悟所動
          posts - 24,comments - 24,trackbacks - 0
          <2009年7月>
          2829301234
          567891011
          12131415161718
          19202122232425
          2627282930311
          2345678

           

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          相冊

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          在JDBC應用中,如果你已經是稍有水平開發者,你就應該始終以PreparedStatement代替Statement.也就是說,在任何時候都不要使用Statement.
          基于以下的原因:
          一.代碼的可讀性和可維護性.
          雖然用PreparedStatement來代替Statement會使代碼多出幾行,但這樣的代碼無論從可讀性還是可維護性上來說.都比直接用Statement的代碼高很多檔次:

          stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");

          perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
          perstmt.setString(1,var1);
          perstmt.setString(2,var2);
          perstmt.setString(3,var3);
          perstmt.setString(4,var4);
          perstmt.executeUpdate();

          不用我多說,對于第一種方法.別說其他人去讀你的代碼,就是你自己過一段時間再去讀,都會覺得傷心.

          二.PreparedStatement盡最大可能提高性能.
          每一種數據庫都會盡最大努力對預編譯語句提供最大的性能優化.因為預編譯語句有可能被重復調用.所以語句在被DB的編譯器編譯后的執行代碼被緩存下來,那么下次調用時只要是相同的預編譯語句就不需要編譯,只要將參數直接傳入編譯過的語句執行代碼中(相當于一個涵數)就會得到執行.這并不是說只有一個 Connection中多次執行的預編譯語句被緩存,而是對于整個DB中,只要預編譯的語句語法和緩存中匹配.那么在任何時候就可以不需要再次編譯而可以直接執行.而statement的語句中,即使是相同一操作,而由于每次操作的數據不同所以使整個語句相匹配的機會極小,幾乎不太可能匹配.比如:
          insert into tb_name (col1,col2) values ('11','22');
          insert into tb_name (col1,col2) values ('11','23');
          即使是相同操作但因為數據內容不一樣,所以整個個語句本身不能匹配,沒有緩存語句的意義.事實是沒有數據庫會對普通語句編譯后的執行代碼緩存.這樣每執行一次都要對傳入的語句編譯一次.

          當然并不是所以預編譯語句都一定會被緩存,數據庫本身會用一種策略,比如使用頻度等因素來決定什么時候不再緩存已有的預編譯結果.以保存有更多的空間存儲新的預編譯語句.

          三.最重要的一點是極大地提高了安全性.

          即使到目前為止,仍有一些人連基本的惡義SQL語法都不知道.
          String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";
          如果我們把[' or '1' = '1]作為varpasswd傳入進來.用戶名隨意,看看會成為什么?

          select * from tb_name = '隨意' and passwd = '' or '1' = '1';
          因為'1'='1'肯定成立,所以可以任何通過驗證.更有甚者:
          把[';drop table tb_name;]作為varpasswd傳入進來,則:
          select * from tb_name = '隨意' and passwd = '';drop table tb_name;有些數據庫是不會讓你成功的,但也有很多數據庫就可以使這些語句得到執行.

          而如果你使用預編譯語句.你傳入的任何內容就不會和原來的語句發生任何匹配的關系.(前提是數據庫本身支持預編譯,但上前可能沒有什么服務端數據庫不支持編譯了,只有少數的桌面數據庫,就是直接文件訪問的那些)只要全使用預編譯語句,你就用不著對傳入的數據做任何過慮.而如果使用普通的statement, 有可能要對drop,;等做費盡心機的判斷和過慮.

          上面的幾個原因,還不足讓你在任何時候都使用PreparedStatement嗎?

           

           

          有的新人可能此時對于用法還不太理解下面給個小例子

          Code Fragment 1:

          String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE ′Colombian′"; 
          stmt.executeUpdate(updateString);

          Code Fragment 2:

          PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? "); 
          updateSales.setInt(1, 75); 
          updateSales.setString(2, "Colombian"); 
          updateSales.executeUpdate();

          set中的1對應第一個? 2對應第二個? 同時注意你set 的類型 是int還是string  哈哈很簡單吧


          原文出處:http://blog.csdn.net/spcusa/archive/2009/05/09/4164076.aspx

          posted @ 2010-12-14 13:58 葉澍成| 編輯 收藏

          //ValueObject類

          public class AdEntity {
              
          private String id;
              
          private String name;
              
          private String broker;
              
          private String date;
              
          private String body;
              
          //get/set
              
              
          public String toString(){
                  
          return "【編號為:"+id+",廣告名稱為:"+name+",代理商為:"+broker+",發布日期為:"+date+",內容為:"+body+"";
              }
          }

          //調用任務類

          public class AdTask implements Callable<AdEntity> {
              @Override
              
          public AdEntity call() throws Exception {
                  
          // 模擬遠程調用花費的一些時間
                  Thread.sleep(5*1000);
                  AdEntity vo
          =new AdEntity();
                  vo.setId(String.valueOf(
          new Random().nextInt(1000)));
                  vo.setName(
          "Ad@內衣廣告");
                  vo.setBroker(
          "CHANNEL");
                  Date date
          =new Date();
                  SimpleDateFormat sdf
          =new SimpleDateFormat("yyyy-MM-dd");
                  String dateStr
          =sdf.format(date);
                  vo.setDate(dateStr);
                  vo.setBody(
          "遠端內容");
                  
          return vo;
              }
          }

          //主函數

          public class TestQueryMemg {
              
              
          /**
               * 
          @param args
               * 
          @throws ExecutionException 
               * 
          @throws InterruptedException 
               
          */
              
          public static void main(String[] args) throws InterruptedException, ExecutionException {
                  ExecutorService exec
          =Executors.newCachedThreadPool();
                  
          //創建Future來完成網絡調用任務
                  Callable<AdEntity> returnAd=new AdTask();
                  Future
          <AdEntity> future=exec.submit(returnAd);
                  
                  
          //開始執行本地化查詢信息
                  AdEntity localAd=new AdEntity();
                  localAd.setId(String.valueOf(
          new Random().nextInt(1000)));
                  localAd.setName(
          "Ad@食品廣告");
                  localAd.setBroker(
          "蒙牛");
                  SimpleDateFormat sdf
          =new SimpleDateFormat("yyyy-MM-dd");
                  String dateStr
          =sdf.format(new Date());
                  localAd.setDate(dateStr);
                  localAd.setBody(
          "內容本地");

                  System.out.println(
          "當前本地化查詢內容為:"+localAd.toString());
                  System.out.println(
          "稍等片刻以獲取遠端信息");
                  
                  
          while(!future.isDone()){
                      
          try {
                          Thread.sleep(
          1*1000);
                      } 
          catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                  AdEntity entityRemote
          =(AdEntity)future.get();

                  System.out.println(
          "合并查詢內容為:\n"+localAd.toString()+"\n"+entityRemote.toString());
              }
          }


           

           

           

          posted @ 2010-12-12 14:44 葉澍成| 編輯 收藏

          個人賬戶類:

          public class PrivateAccount implements Callable {
              Integer total;
              
          public Object call() throws Exception {
                  Thread.sleep(
          5*1000);
                  total
          =new Integer(new Random().nextInt(10000));
                  System.out.println(
          "您個人賬戶上還有"+total+" 存款可以支配");
                  
          return total;
              }
          }

          主函數測試:

          public class SumTest {
              
          /**
               * 
          @param args
               * 
          @throws ExecutionException 
               * 
          @throws InterruptedException 
               
          */
              
          public static void main(String[] args) throws InterruptedException, ExecutionException {
                  Callable privateAccount
          =new PrivateAccount();
                  FutureTask task
          =new FutureTask(privateAccount);
                          
          //創建新線程獲取個人賬戶信息
                  Thread thread=new Thread(task);
                  thread.start();

                  
          int total=new Random().nextInt(1000);
                  System.out.println(
          "主線程在這工作");
                  System.out.println(
          "您目前操作金額為: "+total+" .");
                  System.out.println(
          "請等待計算個人賬戶的金額");
                  
          while(!task.isDone()){//判斷是否已經獲取返回值
                      try {
                          Thread.sleep(
          3*1000);
                      } 
          catch (InterruptedException e) {
                          
          // TODO Auto-generated catch block
                          e.printStackTrace();
                      }
                  }
                  Integer privateSingle
          =(Integer)task.get();
                  
          int post=privateSingle.intValue();
                  
                  System.out.println(
          "您當前賬戶共有金額為:"+(total+post)+" ¥");
              }

          }


           

           

          posted @ 2010-12-10 20:53 葉澍成| 編輯 收藏
          Memcached,人所皆知的remote distribute cache(不知道的可以javaeye一下下,或者google一下下,或者baidu一下下,但是鑒于baidu的排名商業味道太濃(從最近得某某事件可以看出),所以還是建議javaeye一下下),使用起來也非常的簡單,它被用在了很多網站上面,幾乎很少有大型的網站不會使用memcached。

          曾經我也看過很多剖析memcached內部機制的文章,有一點收獲,但是看過之后又忘記了,而且沒有什么深刻的概念,但是最近我遇到一個問題,這個問題迫使我重新來認識memcache,下面我闡述一下我遇到的問題

          問題:我有幾千萬的數據,這些數據會經常被用到,目前來看,它必須要放到memcached中,以保證訪問速度,但是我的memcached中數據經常會有丟失,而業務需求是memcached中的數據是不能丟失的。我的數據丟失的時候,memcached server的內存才使用到60%,也就是還有40%內存被嚴重的浪費掉了。但不是所有的應用都是這樣,其他應用內存浪費的就比較少。為什么內存才使用到60%的時候LRU就執行了呢(之所以確定是LRU執行是因為我發現我的數據丟失的總是前面放進去的,而且這個過程中,這些數據都沒有被訪問,比如第一次訪問的時候,只能訪問第1000w條,而第300w條或者之前的數據都已經丟失了,從日志里看,第300w條肯定是放進去了)。

          帶著這些疑問,我開始重新審視memcached這個產品,首先從它的內存模型開始:我們知道c++里分配內存有兩種方式,預先分配和動態分配,顯然,預先分配內存會使程序比較快,但是它的缺點是不能有效利用內存,而動態分配可以有效利用內存,但是會使程序運行效率下降,memcached的內存分配就是基于以上原理,顯然為了獲得更快的速度,有時候我們不得不以空間換時間。

          也就是說memcached會預先分配內存,對了,memcached分配內存方式稱之為allocator,首先,這里有3個概念:
          1 slab
          2 page
          3 chunk
          解釋一下,一般來說一個memcahced進程會預先將自己劃分為若干個slab,每個slab下又有若干個page,每個page下又有多個chunk,如果我們把這3個咚咚看作是object得話,這是兩個一對多得關系。再一般來說,slab得數量是有限得,幾個,十幾個,或者幾十個,這個跟進程配置得內存有關。而每個slab下得page默認情況是1m,也就是說如果一個slab占用100m得內存得話,那么默認情況下這個slab所擁有得page得個數就是100,而chunk就是我們得數據存放得最終地方。

          舉一個例子,我啟動一個memcached進程,占用內存100m,再打開telnet,telnet localhost 11211,連接上memcache之后,輸入stats  slabs,回車,出現如下數據:
          Java代碼 復制代碼
          1. STAT 1:chunk_size 80  
          2. STAT 1:chunks_per_page 13107  
          3. STAT 1:total_pages 1  
          4. STAT 1:total_chunks 13107  
          5. STAT 1:used_chunks 13107  
          6. STAT 1:free_chunks 0  
          7. STAT 1:free_chunks_end 13107  
          8. STAT 2:chunk_size 100  
          9. STAT 2:chunks_per_page 10485  
          10. STAT 2:total_pages 1  
          11. STAT 2:total_chunks 10485  
          12. STAT 2:used_chunks 10485  
          13. STAT 2:free_chunks 0  
          14. STAT 2:free_chunks_end 10485  
          15. STAT 3:chunk_size 128  
          16. STAT 3:chunks_per_page 8192  
          17. STAT 3:total_pages 1  
          18. STAT 3:total_chunks 8192  
          19. STAT 3:used_chunks 8192  
          20. STAT 3:free_chunks 0  
          21. STAT 3:free_chunks_end 8192  


          以上就是前3個slab得詳細信息
          chunk_size表示數據存放塊得大小,chunks_per_page表示一個內存頁page中擁有得chunk得數量,total_pages表示每個slab下page得個數。total_chunks表示這個slab下chunk得總數(=total_pages * chunks_per_page),used_chunks表示該slab下已經使用得chunk得數量,free_chunks表示該slab下還可以使用得chunks數量。

          從上面得示例slab 1一共有1m得內存空間,而且現在已經被用完了,slab2也有1m得內存空間,也被用完了,slab3得情況依然如此。 而且從這3個slab中chunk得size可以看出來,第一個chunk為80b,第二個是100b,第3個是128b,基本上后一個是前一個得1.25倍,但是這個增長情況我們是可以控制得,我們可以通過在啟動時得進程參數 –f來修改這個值,比如說 –f 1.1表示這個增長因子為1.1,那么第一個slab中得chunk為80b得話,第二個slab中得chunk應該是80*1.1左右。

          解釋了這么多也該可以看出來我遇到得問題得原因了,如果還看不出來,那我再補充關鍵的一句:memcached中新的value過來存放的地址是該value的大小決定的,value總是會被選擇存放到chunk與其最接近的一個slab中,比如上面的例子,如果我的value是80b,那么我這所有的value總是會被存放到1號slab中,而1號slab中的free_chunks已經是0了,怎么辦呢,如果你在啟動memcached的時候沒有追加-M(禁止LRU,這種情況下內存不夠時會out of memory),那么memcached會把這個slab中最近最少被使用的chunk中的數據清掉,然后放上最新的數據。這就解釋了為什么我的內存還有40%的時候LRU就執行了,因為我的其他slab中的chunk_size都遠大于我的value,所以我的value根本不會放到那幾個slab中,而只會放到和我的value最接近的chunk所在的slab中(而這些slab早就滿了,郁悶了)。這就導致了我的數據被不停的覆蓋,后者覆蓋前者。

          問題找到了,解決方案還是沒有找到,因為我的數據必須要求命中率時100%,我只能通過調整slab的增長因子和page的大小來盡量來使命中率接近100%,但是并不能100%保證命中率是100%(這話怎么讀起來這么別扭呢,自我檢討一下自己的語文水平),如果您說,這種方案不行啊,因為我的memcached server不能停啊,不要緊還有另外一個方法,就是memcached-tool,執行move命令,如:move 3 1,代表把3號slab中的一個內存頁移動到1號slab中,有人問了,這有什么用呢,比如說我的20號slab的利用率非常低,但是page卻又很多,比如200,那么就是200m,而2好slab經常發生LRU,明顯page不夠,我就可以move 20 2,把20號slab的一個內存頁移動到2號slab上,這樣就能更加有效的利用內存了(有人說了,一次只移動一個page,多麻煩啊?ahuaxuan說,還是寫個腳本,循環一下吧)。

          有人說不行啊,我的memcache中的數據不能丟失啊,ok,試試新浪的memcachedb吧,雖然我沒有用過,但是建議大家可以試試,它也使利用memcache協議和berkeleyDB做的(寫到這里,我不得不佩服danga了,我覺得它最大的貢獻不是memcache server本身,而是memcache協議),據說它被用在新浪的不少應用上,包括新浪的博客。

          補充,stats slab命令可以查看memcached中slab的情況,而stats命令可以查看你的memcached的一些健康情況,比如說命中率之類的,示例如下:
          Java代碼 復制代碼
          1. STAT pid 2232  
          2. STAT uptime 1348  
          3. STAT time 1218120955  
          4. STAT version 1.2.1  
          5. STAT pointer_size 32  
          6. STAT curr_items 0  
          7. STAT total_items 0  
          8. STAT bytes 0  
          9. STAT curr_connections 1  
          10. STAT total_connections 3  
          11. STAT connection_structures 2  
          12. STAT cmd_get 0  
          13. STAT cmd_set 0  
          14. STAT get_hits 0  
          15. STAT get_misses 0  
          16. STAT bytes_read 26  
          17. STAT bytes_written 16655  
          18. STAT limit_maxbytes 104857600  

          從上面的數據可以看到這個memcached進程的命中率很好,get_misses低達0個,怎么回事啊,因為這個進程使我剛啟動的,我只用telnet連了一下,所以curr_connections為1,而total_items為0,因為我沒有放數據進去,get_hits為0,因為我沒有調用get方法,最后的結果就是misses當然為0,哇哦,換句話說命中率就是100%,又yy了。

          該到總結的時候了,從這篇文章里我們可以得到以下幾個結論:
          結論一,memcached得LRU不是全局的,而是針對slab的,可以說是區域性的。
          結論二,要提高memcached的命中率,預估我們的value大小并且適當的調整內存頁大小和增長因子是必須的。
          結論三,帶著問題找答案理解的要比隨便看看的效果好得多。
          posted @ 2010-08-17 17:45 葉澍成| 編輯 收藏

          1. 關閉所有oracle的服務和程序
          2. 選擇開始| 程序|oracle Installation Products命令,運行Universal Installer,彈出歡迎對話框
          3. 單機 卸載產品 按鈕,彈出Inventory對話框
          4. 選中Inventroy對話框中的所有節點,點擊刪除,確認即可
          5. 選 擇 開始|運行 輸入regedit并按ENTER鍵,選擇HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE,刪除此象,然后選擇 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services,滾動此列表,刪除與oracle有關的所 有選項。
          6. 從桌面上、STARTUP和程序菜單中刪除所有ORACLE的組和圖標。
          7. 重啟系統。
          8. 刪除包括文件在內的安裝目錄。至此ORACLE的全部卸載完成。
          posted @ 2010-05-19 18:35 葉澍成| 編輯 收藏
          位運算應用口訣

          清零取位要用與,某位置一可用或

          若要取反和交換,輕輕松松用異或

          移位運算

          要點 1 它們都是雙目運算符,兩個運算分量都是整形,結果也是整形。

               2 "<<" 左移:右邊空出的位上補0,左邊的位將從字頭擠掉,其值相當于乘2。

               3 ">>"右移:右邊的位被擠掉。對于左邊移出的空位,如果是正數則空位補0,若為負數,可能補0或補1,這取決于所用的計算機系統。

               4 ">>>"運算符,右邊的位被擠掉,對于左邊移出的空位一概補上0。

          位運算符的應用 (源操作數s 掩碼mask)

          (1) 按位與-- &

          1 清零特定位 (mask中特定位置0,其它位為1,s=s&mask)

          2 取某數中指定位 (mask中特定位置1,其它位為0,s=s&mask)

          (2) 按位或-- |

              常用來將源操作數某些位置1,其它位不變。 (mask中特定位置1,其它位為0 s=s|mask)

          (3) 位異或-- ^

          1 使特定位的值取反 (mask中特定位置1,其它位為0 s=s^mask)

          2 不引入第三變量,交換兩個變量的值 (設 a=a1,b=b1)

              目 標           操 作              操作后狀態

          a=a1^b1         a=a^b              a=a1^b1,b=b1

          b=a1^b1^b1      b=a^b              a=a1^b1,b=a1

          a=b1^a1^a1      a=a^b              a=b1,b=a1

          二進制補碼運算公式:

          -x = ~x + 1 = ~(x-1)

          ~x = -x-1

          -(~x) = x+1

          ~(-x) = x-1

          x+y = x - ~y - 1 = (x|y)+(x&y)

          x-y = x + ~y + 1 = (x|~y)-(~x&y)

          x^y = (x|y)-(x&y)

          x|y = (x&~y)+y

          x&y = (~x|y)-~x

          x==y:    ~(x-y|y-x)

          x!=y:    x-y|y-x

          x< y:    (x-y)^((x^y)&((x-y)^x))

          x<=y:    (x|~y)&((x^y)|~(y-x))

          x< y:    (~x&y)|((~x|y)&(x-y))//無符號x,y比較

          x<=y:    (~x|y)&((x^y)|~(y-x))//無符號x,y比較

          應用舉例

          (1) 判斷int型變量a是奇數還是偶數           

          a&1   = 0 偶數

                 a&1 =   1 奇數

          (2) 取int型變量a的第k位 (k=0,1,2……sizeof(int)),即a>>k&1

          (3) 將int型變量a的第k位清0,即a=a&~(1<<k)

          (4) 將int型變量a的第k位置1, 即a=a|(1<<k)

          (5) int型變量循環左移k次,即a=a<<k|a>>16-k   (設sizeof(int)=16)

          (6) int型變量a循環右移k次,即a=a>>k|a<<16-k   (設sizeof(int)=16)

          (7)整數的平均值

          對于兩個整數x,y,如果用 (x+y)/2 求平均值,會產生溢出,因為 x+y 可能會大于INT_MAX,但是我們知道它們的平均值是肯定不會溢出的,我們用如下算法:

          int average(int x, int y)   //返回X,Y 的平均值

          {   

               return (x&y)+((x^y)>>1);

          }

          (8)判斷一個整數是不是2的冪,對于一個數 x >= 0,判斷他是不是2的冪

          boolean power2(int x)

          {

              return ((x&(x-1))==0)&&(x!=0);

          }

          (9)不用temp交換兩個整數

          void swap(int x , int y)

          {

              x ^= y;

              y ^= x;

              x ^= y;

          }

          (10)計算絕對值

          int abs( int x )

          {

          int y ;

          y = x >> 31 ;

          return (x^y)-y ;        //or: (x+y)^y

          }

          (11)取模運算轉化成位運算 (在不產生溢出的情況下)

                   a % (2^n) 等價于 a & (2^n - 1)

          (12)乘法運算轉化成位運算 (在不產生溢出的情況下)

                   a * (2^n) 等價于 a<< n

          (13)除法運算轉化成位運算 (在不產生溢出的情況下)

                   a / (2^n) 等價于 a>> n

                  例: 12/8 == 12>>3

          (14) a % 2 等價于 a & 1       

          (15) if (x == a) x= b;

                      else x= a;

                  等價于 x= a ^ b ^ x;

          (16) x 的 相反數 表示為 (~x+1)

          posted @ 2010-03-30 13:59 葉澍成 閱讀(414) | 評論 (0)編輯 收藏
           

          線程,是指正在執行的一個指點令序列。在java平臺上是指從一個線程對象的start()開始。運行run方法體中的那一段相對獨立的過程。

          線程的并發與并行

          在過去的電腦都已單CPU作為主要的處理方式,無論是PC或者是服務器都是如此。系統調用某一個時刻只能有一個線程運行。當然這當中采用了比較多的策略來做時間片輪詢。通過不斷的調度切換來運行線程運行,而這種方式就叫做并發(concurrent)。

          隨著工藝水平的逐漸提升,CPU的技術也在不斷增進。因此在如今多個CPU已經不是什么特別的,而大家常常以SMP的方式來形容多個CPU來處理兩個或者兩個以上的線程運行方式就稱為并行(parallel)。

          JAVA線程對象

                                                    繼承Thread,實現start()方法

          要實現線程運行,JAVA中有兩種方式:

                                                實現Runnable,然后再傳遞給Thread實例

          注意:線程對象和線程是兩個截然不同的概念。

          線程對象JVM產生的一個普通的Object子類

          線程是CPU分配給這個對象的一個運行過程

          public class Test {

           public static void main(String[] args) throws Exception{

              MyThread mt = new MyThread();

              mt.start();

              mt.join();

              Thread.sleep(3000);

              mt.start();

           }

          }

          當線程對象mt運行完成后,我們讓主線程休息一下,然后我們再次在這個線程對象上啟動線程.結果我們看到:

          Exception in thread "main" java.lang.IllegalThreadStateException

          根本原因是在以下源代碼中找出:

          public synchronized void start() {

                  if (started)
                      throw new IllegalThreadStateException();

                  started = true;

                 group.add(this);

                  start0();

           }

          一個Thread的實例一旦調用start()方法,這個實例的started標記就標記為true,事實中不管這個線程后來有沒有執行到底,只要調用了一次start()就再也沒有機會運行了,這意味著:

          【通過Thread實例的start(),一個Thread的實例只能產生一個線程】

          interrupt()方法

          當一個線程對象調用interrupt()方法,它對應的線程并沒有被中斷,只是改變了它的中斷狀態。使當前線程的狀態變以中斷狀態,如果沒有其它影響,線程還會自己繼續執行。只有當線程執行到sleepwaitjoin等方法時,或者自己檢查中斷狀態而拋出異常的情況下,線程才會拋出異常。

          join()方法

          join()方法,正如第一節所言,在一個線程對象上調用join方法,是當前線程等待這個線程對象對應的線程結束

          例如:有兩個工作,工作A要耗時10秒鐘,工作B要耗時10秒或更多。我們在程序中先生成一個線程去做工作B,然后做工作A

                  new B().start();//做工作B

          A();//做工作A

          工作A完成后,下面要等待工作B的結果來進行處理。如果工作B還沒有完成我就不能進行下面的工作C,所以:

          B b = new B();

          b.start();//做工作B

          A();//做工作A

          b.join();//等工作B完成.

          C();//繼續工作C

          原則:【join是測試其它工作狀態的唯一正確方法】

          yield()方法

          yield()方法也是類方法,只在當前線程上調用,理由同上,它主是讓當前線程放棄本次分配到的時間片,調用這個方法不會提高任何效率,只是降低了CPU的總周期上面介紹的線程一些方法,基于(基礎篇)而言只能簡單提及。以后具體應用中我會結合實例詳細論述。

          原則:【不是非常必要的情況下,沒有理由調用它】

          wait()notify()/notityAll()方法

          首先明確一點他們的屬于普通對象方法,并非是線程對象方法;其次它們只能在同步方法中調用。線程要想調用一個對象的wait()方法就要先獲得該對象的監視鎖,而一旦調用wait()后又立即釋放該鎖。

          線程的互斥控制

          多個線程同時操作某一對象時,一個線程對該對象的操作可能會改變其狀態,而該狀態會影響另一線程對該對象的真正結果。

          synchornized關鍵字

          把一個單元聲明為synchornized,就可以讓在同一時間只有一個線程操作該方法。作為記憶可以把synchronized看作是一個鎖。但是我們要理解鎖是被動的,還是主動的呢?換而言之它到底鎖什么了?鎖誰了?

          例如:

          synchronized(obj){

                           //todo…

          }

          如果代碼運行到此處,synchronized首先獲取obj參數對象的鎖,若沒有獲取線程只能等待,如果多個線程運行到這只能有一個線程獲取obj的鎖,然后再執行{}中的代碼。因此obj作用范圍不同,控制程序也不同。

          如果一個方法聲明為synchornized的,則等同于把在為個方法上調用synchornized(this)

          如果一個靜態方法被聲明為synchornized,則等同于把在為個方法上調用synchornized(.class)

           

          真正的停止線程

          要讓一個線程得到真正意義的停止,需要了解當前的線程在干什么,如果線程當前沒有做什么,那立刻讓對方退出,當然是沒有任何問題,但是如果對方正在手頭趕工,那就必須讓他停止,然后收拾殘局。因此,首先需要了解步驟:

          1.     正常運行;

          2.       處理結束前的工作,也就是準備結束;

          3.       結束退出。

          注:以上部分概括出自某位牛人大哥的筆記,經常拜讀他的博客
          posted @ 2009-07-20 10:00 葉澍成 閱讀(393) | 評論 (0)編輯 收藏
           

          對于這本書的閱讀,說來也很慚愧。過去黎敏基本對于他翻譯過的書,都有郵寄送給我,書拿到手后,都是內心的高興——哇,不用花錢的書,爽!(自己YY下)唯一要求就是能夠讀后能寫個讀后感,但是很多次都抱歉的忽悠了他。盡管如此他這次翻譯的書還是一如既往的郵寄給我(當然也是在我厚臉皮的去索要的前提下,哈哈),同樣答應寫一篇讀后感,可是遲到的讀后感一直到現在才肯出爐,確實有點對不住他。這本書是在我每天早早擠車一個星期多讀出來的,那個汽車真叫熱啊,嘿嘿。切入正題,談談對于本書的一個看法和意見。

                 這本書整體風格基本還是沿襲了第一版本的套路,把每個重點都寫成一個條目來針對性的闡述,本書總共條目為七十八條(第一版書共有五十七條)。當中很多是在JDK5基礎上作為第一版的更新(與第一版比較明顯特征是把原有第一版的第五章,第六章的結構化特征和方法換成JDK5的新特性:泛型,枚舉,方法)。

                 這本書我到現在都懷疑出版社沒有花很多時間在排版上,為什么這么說?在黎敏在翻譯階段也就有拿書的樣稿給我看過,我第一看到就是封面問題——居然是黃色的,當時就跟他提出來,這個需要他和出版社去很好的斟酌一番。當時黎敏也說已經跟出版社商量過,哪知道最后拿到手上的居然還是“黃色”。可見出版社不知道是出于什么來考慮,讓我來猜想下:難道是怕大伙都是色盲非要用黃色來提醒大家嗎?更為失望的居然標題使用紅色,暈啊!其次就要說書紙張也未免太扯淡了吧?我第一感覺紙張就是草稿紙一般,實在無語。在代碼處理方面,顯得不如第一版的那種爽朗,是不是出版社考慮節省紙張啊?非要把很多代碼的間隔弄的特小,這樣對于可讀性來說確實很有疲勞感。所以這里向提醒以后的出版社——你忽悠是可以,但是別把讀者當傻瓜。

                 上面是談到在書的編排和效果的問題。現在談談書中內容的一些感受。整體上說書翻譯還是可以,不過當中也不乏一些乏味用詞過當的問題,這里要說到最明顯的就是書中出現很多在每一個條目后的總結詞匯都或多或少帶有“本條目”一詞,個人覺得偶爾寫寫是沒很大問題,但是過多重復顯得機械化的審美疲勞,甚至有時候過多這樣的詞匯對于閱讀流暢性稍微欠佳。像這樣類似的詞匯還有——“不嚴格地講”,“簡而言之”,這些詞匯確實稍微影響閱讀感。這里小糾下不爽或者錯誤的位置:P240處第二段第三行和第四行中出現兩個“現在”,這個可能在校正時沒有很好去潤色。P216倒數第二段第三行有一個估計是印刷錯誤:【原】僅僅一個異常就會導致該方法不得不如果沒有錯的話,這個字應該是:處。

          還有類似第二章談到的:靜態工廠方法與構造器不同的第三的優勢在于,它們可以返回原返回類型的任何子類型的對象。就這句話,老實講我真看了很久才明白啥意思。

                 以上是談到一些不足的細微之處,不過閱讀此書后,確實對于以前一些不起眼的所謂了解語法也很好的得到一次重新的認識。比如以前在使用非檢測警告上,以前很習慣的直接在整個類上直接使用標記表示;對于了解for-each的循環上得到進一步的認識;對于枚舉類型的使用上更加靈活。這些都是個人對于此書泛讀之后的一些淺薄的看法。如果對于譯者有不敬之處還望原諒!

          posted @ 2009-07-17 18:09 葉澍成 閱讀(324) | 評論 (0)編輯 收藏
          主站蜘蛛池模板: 资溪县| 汉川市| 白沙| 大同市| 息烽县| 乾安县| 蒙城县| 越西县| 曲靖市| 唐山市| 金湖县| 闸北区| 蒲城县| 天祝| 渭南市| 车致| 金沙县| 祁东县| 铁力市| 华阴市| 常宁市| 武安市| 玉林市| 方正县| 阿瓦提县| 嘉黎县| 石城县| 淳化县| 洞头县| 海晏县| 化德县| 左贡县| 曲周县| 胶州市| 沙雅县| 东丽区| 晋中市| 杭锦后旗| 凌海市| 松江区| 中阳县|