<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    yeshucheng
    追逐自己,追逐方向,心隨悟所動
    posts - 24,comments - 24,trackbacks - 0

    在JDBC應用中,如果你已經(jīng)是稍有水平開發(fā)者,你就應該始終以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盡最大可能提高性能.
    每一種數(shù)據(jù)庫都會盡最大努力對預編譯語句提供最大的性能優(yōu)化.因為預編譯語句有可能被重復調(diào)用.所以語句在被DB的編譯器編譯后的執(zhí)行代碼被緩存下來,那么下次調(diào)用時只要是相同的預編譯語句就不需要編譯,只要將參數(shù)直接傳入編譯過的語句執(zhí)行代碼中(相當于一個涵數(shù))就會得到執(zhí)行.這并不是說只有一個 Connection中多次執(zhí)行的預編譯語句被緩存,而是對于整個DB中,只要預編譯的語句語法和緩存中匹配.那么在任何時候就可以不需要再次編譯而可以直接執(zhí)行.而statement的語句中,即使是相同一操作,而由于每次操作的數(shù)據(jù)不同所以使整個語句相匹配的機會極小,幾乎不太可能匹配.比如:
    insert into tb_name (col1,col2) values ('11','22');
    insert into tb_name (col1,col2) values ('11','23');
    即使是相同操作但因為數(shù)據(jù)內(nèi)容不一樣,所以整個個語句本身不能匹配,沒有緩存語句的意義.事實是沒有數(shù)據(jù)庫會對普通語句編譯后的執(zhí)行代碼緩存.這樣每執(zhí)行一次都要對傳入的語句編譯一次.

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

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

    即使到目前為止,仍有一些人連基本的惡義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;有些數(shù)據(jù)庫是不會讓你成功的,但也有很多數(shù)據(jù)庫就可以使這些語句得到執(zhí)行.

    而如果你使用預編譯語句.你傳入的任何內(nèi)容就不會和原來的語句發(fā)生任何匹配的關系.(前提是數(shù)據(jù)庫本身支持預編譯,但上前可能沒有什么服務端數(shù)據(jù)庫不支持編譯了,只有少數(shù)的桌面數(shù)據(jù)庫,就是直接文件訪問的那些)只要全使用預編譯語句,你就用不著對傳入的數(shù)據(jù)做任何過慮.而如果使用普通的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+",發(fā)布日期為:"+date+",內(nèi)容為:"+body+"";
        }
    }

    //調(diào)用任務類

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

    //主函數(shù)

    public class TestQueryMemg {
        
        
    /**
         * 
    @param args
         * 
    @throws ExecutionException 
         * 
    @throws InterruptedException 
         
    */
        
    public static void main(String[] args) throws InterruptedException, ExecutionException {
            ExecutorService exec
    =Executors.newCachedThreadPool();
            
    //創(chuàng)建Future來完成網(wǎng)絡調(diào)用任務
            Callable<AdEntity> returnAd=new AdTask();
            Future
    <AdEntity> future=exec.submit(returnAd);
            
            
    //開始執(zhí)行本地化查詢信息
            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(
    "內(nèi)容本地");

            System.out.println(
    "當前本地化查詢內(nèi)容為:"+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èi)容為:\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;
        }
    }

    主函數(shù)測試:

    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);
                    
    //創(chuàng)建新線程獲取個人賬戶信息
            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()){//判斷是否已經(jīng)獲取返回值
                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的排名商業(yè)味道太濃(從最近得某某事件可以看出),所以還是建議javaeye一下下),使用起來也非常的簡單,它被用在了很多網(wǎng)站上面,幾乎很少有大型的網(wǎng)站不會使用memcached。

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

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

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

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

    舉一個例子,我啟動一個memcached進程,占用內(nèi)存100m,再打開telnet,telnet localhost 11211,連接上memcache之后,輸入stats  slabs,回車,出現(xiàn)如下數(shù)據(jù):
    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表示數(shù)據(jù)存放塊得大小,chunks_per_page表示一個內(nèi)存頁page中擁有得chunk得數(shù)量,total_pages表示每個slab下page得個數(shù)。total_chunks表示這個slab下chunk得總數(shù)(=total_pages * chunks_per_page),used_chunks表示該slab下已經(jīng)使用得chunk得數(shù)量,free_chunks表示該slab下還可以使用得chunks數(shù)量。

    從上面得示例slab 1一共有1m得內(nèi)存空間,而且現(xiàn)在已經(jīng)被用完了,slab2也有1m得內(nèi)存空間,也被用完了,slab3得情況依然如此。 而且從這3個slab中chunk得size可以看出來,第一個chunk為80b,第二個是100b,第3個是128b,基本上后一個是前一個得1.25倍,但是這個增長情況我們是可以控制得,我們可以通過在啟動時得進程參數(shù) –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已經(jīng)是0了,怎么辦呢,如果你在啟動memcached的時候沒有追加-M(禁止LRU,這種情況下內(nèi)存不夠時會out of memory),那么memcached會把這個slab中最近最少被使用的chunk中的數(shù)據(jù)清掉,然后放上最新的數(shù)據(jù)。這就解釋了為什么我的內(nèi)存還有40%的時候LRU就執(zhí)行了,因為我的其他slab中的chunk_size都遠大于我的value,所以我的value根本不會放到那幾個slab中,而只會放到和我的value最接近的chunk所在的slab中(而這些slab早就滿了,郁悶了)。這就導致了我的數(shù)據(jù)被不停的覆蓋,后者覆蓋前者。

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

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

    補充,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  

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

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

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

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

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

    移位運算

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

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

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

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

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

    (1) 按位與-- &

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

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

    (2) 按位或-- |

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

    (3) 位異或-- ^

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

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

        目 標           操 作              操作后狀態(tài)

    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是奇數(shù)還是偶數(shù)           

    a&1   = 0 偶數(shù)

           a&1 =   1 奇數(shù)

    (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型變量循環(huán)左移k次,即a=a<<k|a>>16-k   (設sizeof(int)=16)

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

    (7)整數(shù)的平均值

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

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

    {   

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

    }

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

    boolean power2(int x)

    {

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

    }

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

    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)取模運算轉化成位運算 (在不產(chǎn)生溢出的情況下)

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

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

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

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

             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 的 相反數(shù) 表示為 (~x+1)

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

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

    線程的并發(fā)與并行

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

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

    JAVA線程對象

                                              繼承Thread,實現(xiàn)start()方法

    要實現(xiàn)線程運行,JAVA中有兩種方式:

                                          實現(xiàn)Runnable,然后再傳遞給Thread實例

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

    線程對象JVM產(chǎn)生的一個普通的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的實例一旦調(diào)用start()方法,這個實例的started標記就標記為true,事實中不管這個線程后來有沒有執(zhí)行到底,只要調(diào)用了一次start()就再也沒有機會運行了,這意味著:

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

    interrupt()方法

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

    join()方法

    join()方法,正如第一節(jié)所言,在一個線程對象上調(diào)用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();//繼續(xù)工作C

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

    yield()方法

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

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

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

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

    線程的互斥控制

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

    synchornized關鍵字

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

    例如:

    synchronized(obj){

                     //todo…

    }

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

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

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

     

    真正的停止線程

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

    1.     正常運行;

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

    3.       結束退出。

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

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

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

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

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

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

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

    posted @ 2009-07-17 18:09 葉澍成 閱讀(315) | 評論 (0)編輯 收藏
         摘要: Xml Schema的用途 1. 定義一個Xml文檔中都有什么元素 2. 定義一個Xml文檔中都會有什么屬性 3. 定義某個節(jié)點的都有什么樣的子節(jié)點,可以有多少個子節(jié)點,子節(jié)點出現(xiàn)的順序 4. 定義元素或者屬性的數(shù)據(jù)類型 5. 定義元素或者屬性的默認值或者固定值 Xml Schema的根元素: <?xml version="1....  閱讀全文
    posted @ 2009-02-24 11:18 葉澍成| 編輯 收藏
    引言                                       

    HTTP是一個屬于應用層的面向對象的協(xié)議,由于其簡捷、快速的方式,適用于分布式超媒體信息系統(tǒng)。它于1990年提出,經(jīng)過幾年的
    使用與發(fā)展,得到不斷地完善和擴展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的規(guī)范化工作正在進行之中,而且HTTP-
    NG(Next Generation of HTTP)的建議已經(jīng)提出。
    HTTP協(xié)議的主要特點可概括如下:
    1.支持客戶/服務器模式。
    2.簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規(guī)定了客戶與服
    務器聯(lián)系的類型不同。由于HTTP協(xié)議簡單,使得HTTP服務器的程序規(guī)模小,因而通信速度很快。
    3.靈活:HTTP允許傳輸任意類型的數(shù)據(jù)對象。正在傳輸?shù)念愋陀蒀ontent-Type加以標記。
    4.無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,并收到客戶的應答后,即斷開連接。采用這種方
    式可以節(jié)省傳輸時間。
    5.無狀態(tài):HTTP協(xié)議是無狀態(tài)協(xié)議。無狀態(tài)是指協(xié)議對于事務處理沒有記憶能力。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息,則
    它必須重傳,這樣可能導致每次連接傳送的數(shù)據(jù)量增大。另一方面,在服務器不需要先前信息時它的應答就較快。

    一、HTTP協(xié)議詳解之URL篇

        http(超文本傳輸協(xié)議)是一個基于請求與響應模式的、無狀態(tài)的、應用層的協(xié)議,常基于TCP的連接方
    式,HTTP1.1版本中給出一種持續(xù)連接的機制,絕大多數(shù)的Web開發(fā),都是構建在HTTP協(xié)議之上的Web應用。

    HTTP URL (URL是一種特殊類型的URI,包含了用于查找某個資源的足夠的信息)的格式如下:
    http://host[":"port][abs_path]
    http表示要通過HTTP協(xié)議來定位網(wǎng)絡資源;host表示合法的Internet主機域名或者IP地址;port指定一個端口號,
    為空則使用缺省端口80;abs_path指定請求資源的URI;如果URL中沒有給出abs_path,那么當它作為請求URI
    時,必須以“/”的形式給出,通常這個工作瀏覽器自動幫我們完成。
    eg:
    1、輸入:
    www.guet.edu.cn
    瀏覽器自動轉換成:http://www.guet.edu.cn/
    2、http:192.168.0.116:8080/index.jsp 

    二、HTTP協(xié)議詳解之請求篇

        http請求由三部分組成,分別是:請求行、消息報頭、請求正文

    1、請求行以一個方法符號開頭,以空格分開,后面跟著請求的URI和協(xié)議的版本,格式如下:Method Request-
    URI HTTP-Version CRLF 
    其中 Method表示請求方法;Request-URI是一個統(tǒng)一資源標識符;HTTP-Version表示請求的HTTP協(xié)議版本;
    CRLF表示回車和換行(除了作為結尾的CRLF外,不允許出現(xiàn)單獨的CR或LF字符)。

    請求方法(所有方法全為大寫)有多種,各個方法的解釋如下:
    GET     請求獲取Request-URI所標識的資源
    POST    在Request-URI所標識的資源后附加新的數(shù)據(jù)
    HEAD    請求獲取由Request-URI所標識的資源的響應消息報頭
    PUT     請求服務器存儲一個資源,并用Request-URI作為其標識
    DELETE  請求服務器刪除Request-URI所標識的資源
    TRACE   請求服務器回送收到的請求信息,主要用于測試或診斷
    CONNECT 保留將來使用
    OPTIONS 請求查詢服務器的性能,或者查詢與資源相關的選項和需求
    應用舉例:
    GET方法:在瀏覽器的地址欄中輸入網(wǎng)址的方式訪問網(wǎng)頁時,瀏覽器采用GET方法向服務器獲取資源,
    eg:GET /form.html HTTP/1.1 (CRLF)

    POST方法要求被請求服務器接受附在請求后面的數(shù)據(jù),常用于提交表單。
    eg:POST /reg.jsp HTTP/ (CRLF)
    Accept:image/gif,image/x-xbit,... (CRLF)
    ...
    HOST:www.guet.edu.cn (CRLF)
    Content-Length:22 (CRLF)
    Connection:Keep-Alive (CRLF)
    Cache-Control:no-cache (CRLF)
    (CRLF)         //該CRLF表示消息報頭已經(jīng)結束,在此之前為消息報頭
    user=jeffrey&pwd=1234  //此行以下為提交的數(shù)據(jù)

    HEAD方法與GET方法幾乎是一樣的,對于HEAD請求的回應部分來說,它的HTTP頭部中包含的信息與通過
    GET請求所得到的信息是相同的。利用這個方法,不必傳輸整個資源內(nèi)容,就可以得到Request-URI所標識的資
    源的信息。該方法常用于測試超鏈接的有效性,是否可以訪問,以及最近是否更新。
    2、請求報頭后述
    3、請求正文(略) 

    三、HTTP協(xié)議詳解之響應篇

        在接收和解釋請求消息后,服務器返回一個HTTP響應消息。

    HTTP響應也是由三個部分組成,分別是:狀態(tài)行、消息報頭、響應正文
    1、狀態(tài)行格式如下:
    HTTP-Version Status-Code Reason-Phrase CRLF
    其中,HTTP-Version表示服務器HTTP協(xié)議的版本;Status-Code表示服務器發(fā)回的響應狀態(tài)代碼;Reason-Phrase
    表示狀態(tài)代碼的文本描述。
    狀態(tài)代碼有三位數(shù)字組成,第一個數(shù)字定義了響應的類別,且有五種可能取值:
    1xx:指示信息--表示請求已接收,繼續(xù)處理
    2xx:成功--表示請求已被成功接收、理解、接受
    3xx:重定向--要完成請求必須進行更進一步的操作
    4xx:客戶端錯誤--請求有語法錯誤或請求無法實現(xiàn)
    5xx:服務器端錯誤--服務器未能實現(xiàn)合法的請求
    常見狀態(tài)代碼、狀態(tài)描述、說明:
    200 OK      //客戶端請求成功
    400 Bad Request  //客戶端請求有語法錯誤,不能被服務器所理解
    401 Unauthorized //請求未經(jīng)授權,這個狀態(tài)代碼必須和WWW-Authenticate報                 //頭域一起使用
    403 Forbidden  //服務器收到請求,但是拒絕提供服務
    404 Not Found  //請求資源不存在,eg:輸入了錯誤的URL
    500 Internal Server Error //服務器發(fā)生不可預期的錯誤
    503 Server Unavailable  //服務器當前不能處理客戶端的請求,一段時間后,                         //可能恢復正常
    eg:HTTP/1.1 200 OK (CRLF)

    2、響應報頭后述

    3、響應正文就是服務器返回的資源的內(nèi)容 

    四、HTTP協(xié)議詳解之消息報頭篇

        HTTP消息由客戶端到服務器的請求和服務器到客戶端的響應組成。請求消息和響應消息都是由開始行(對
    于請求消息,開始行就是請求行,對于響應消息,開始行就是狀態(tài)行),消息報頭(可選),空行(只有
    CRLF的行),消息正文(可選)組成。

    HTTP消息報頭包括普通報頭、請求報頭、響應報頭、實體報頭。
    每一個報頭域都是由名字+“:”+空格+值 組成,消息報頭域的名字是大小寫無關的。

    1、普通報頭
    在普通報頭中,有少數(shù)報頭域用于所有的請求和響應消息,但并不用于被傳輸?shù)膶嶓w,只用于傳輸?shù)南ⅰ?br /> eg:
    Cache-Control   用于指定緩存指令,緩存指令是單向的(響應中出現(xiàn)的緩存指令在請求中未必會出現(xiàn)),且是
    獨立的(一個消息的緩存指令不會影響另一個消息處理的緩存機制),HTTP1.0使用的類似的報頭域為Pragma。
    請求時的緩存指令包括:no-cache(用于指示請求或響應消息不能緩存)、no-store、max-age、max-stale、min-
    fresh、only-if-cached;
    響應時的緩存指令包括:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、
    max-age、s-maxage.
    eg:為了指示IE瀏覽器(客戶端)不要緩存頁面,服務器端的JSP程序可以編寫如下:response.sehHeader
    ("Cache-Control","no-cache");
    //response.setHeader("Pragma","no-cache");作用相當于上述代碼,通常兩者//合用
    這句代碼將在發(fā)送的響應消息中設置普通報頭域:Cache-Control:no-cache


    Date普通報頭域表示消息產(chǎn)生的日期和時間

    Connection普通報頭域允許發(fā)送指定連接的選項。例如指定連接是連續(xù),或者指定“close”選項,通知服務
    器,在響應完成后,關閉連接

    2、請求報頭
    請求報頭允許客戶端向服務器端傳遞請求的附加信息以及客戶端自身的信息。
    常用的請求報頭
    Accept
    Accept請求報頭域用于指定客戶端接受哪些類型的信息。eg:Accept:image/gif,表明客戶端希望接受GIF圖象
    格式的資源;Accept:text/html,表明客戶端希望接受html文本。
    Accept-Charset
    Accept-Charset請求報頭域用于指定客戶端接受的字符集。eg:Accept-Charset:iso-8859-1,gb2312.如果在請求消
    息中沒有設置這個域,缺省是任何字符集都可以接受。
    Accept-Encoding
    Accept-Encoding請求報頭域類似于Accept,但是它是用于指定可接受的內(nèi)容編碼。eg:Accept-Encoding:gzip.deflate.如果請求消息中沒有設置這個域服務器假定客戶端對各種內(nèi)容編碼都可以接受。
    Accept-Language
    Accept-Language請求報頭域類似于Accept,但是它是用于指定一種自然語言。eg:Accept-Language:zh-cn.如果請
    求消息中沒有設置這個報頭域,服務器假定客戶端對各種語言都可以接受。
    Authorization
    Authorization請求報頭域主要用于證明客戶端有權查看某個資源。當瀏覽器訪問一個頁面時,如果收到服務器
    的響應代碼為401(未授權),可以發(fā)送一個包含Authorization請求報頭域的請求,要求服務器對其進行驗證。
    Host(發(fā)送請求時,該報頭域是必需的)
    Host請求報頭域主要用于指定被請求資源的Internet主機和端口號,它通常從HTTP URL中提取出來的,eg:
    我們在瀏覽器中輸入:
    http://www.guet.edu.cn/index.html
    瀏覽器發(fā)送的請求消息中,就會包含Host請求報頭域,如下:
    Host:
    www.guet.edu.cn
    此處使用缺省端口號80,若指定了端口號,則變成:Host:www.guet.edu.cn:指定端口號
    User-Agent
    我們上網(wǎng)登陸論壇的時候,往往會看到一些歡迎信息,其中列出了你的操作系統(tǒng)的名稱和版本,你所使用的
    瀏覽器的名稱和版本,這往往讓很多人感到很神奇,實際上,服務器應用程序就是從User-Agent這個請求報頭
    域中獲取到這些信息。User-Agent請求報頭域允許客戶端將它的操作系統(tǒng)、瀏覽器和其它屬性告訴服務器。不
    過,這個報頭域不是必需的,如果我們自己編寫一個瀏覽器,不使用User-Agent請求報頭域,那么服務器端就
    無法得知我們的信息了。
    請求報頭舉例:
    GET /form.html HTTP/1.1 (CRLF)
    Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-
    powerpoint,application/msword,*/* (CRLF)
    Accept-Language:zh-cn (CRLF)
    Accept-Encoding:gzip,deflate (CRLF)
    If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF)
    If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF)
    User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF)
    Host:www.guet.edu.cn (CRLF)
    Connection:Keep-Alive (CRLF)
    (CRLF)

    3、響應報頭
    響應報頭允許服務器傳遞不能放在狀態(tài)行中的附加響應信息,以及關于服務器的信息和對Request-URI所標識
    的資源進行下一步訪問的信息。
    常用的響應報頭
    Location
    Location響應報頭域用于重定向接受者到一個新的位置。Location響應報頭域常用在更換域名的時候。
    Server
    Server響應報頭域包含了服務器用來處理請求的軟件信息。與User-Agent請求報頭域是相對應的。下面是
    Server響應報頭域的一個例子:
    Server:Apache-Coyote/1.1
    WWW-Authenticate
    WWW-Authenticate響應報頭域必須被包含在401(未授權的)響應消息中,客戶端收到401響應消息時候,并發(fā)
    送Authorization報頭域請求服務器對其進行驗證時,服務端響應報頭就包含該報頭域。
    eg:WWW-Authenticate:Basic realm="Basic Auth Test!"  //可以看出服務器對請求資源采用的是基本驗證機制。


    4、實體報頭
    請求和響應消息都可以傳送一個實體。一個實體由實體報頭域和實體正文組成,但并不是說實體報頭域和實體正文要在一起發(fā)送,可以只發(fā)送實體報頭域。實體報頭定義了關于實體正文(eg:有無實體正文)和請求所標識的資源的元信息。
    常用的實體報頭
    Content-Encoding
    Content-Encoding實體報頭域被用作媒體類型的修飾符,它的值指示了已經(jīng)被應用到實體正文的附加內(nèi)容的編
    碼,因而要獲得Content-Type報頭域中所引用的媒體類型,必須采用相應的解碼機制。Content-Encoding這樣用
    于記錄文檔的壓縮方法,eg:Content-Encoding:gzip
    Content-Language
    Content-Language實體報頭域描述了資源所用的自然語言。沒有設置該域則認為實體內(nèi)容將提供給所有的語言
    閱讀
    者。eg:Content-Language:da
    Content-Length
    Content-Length實體報頭域用于指明實體正文的長度,以字節(jié)方式存儲的十進制數(shù)字來表示。
    Content-Type
    Content-Type實體報頭域用語指明發(fā)送給接收者的實體正文的媒體類型。eg:
    Content-Type:text/html;charset=ISO-8859-1
    Content-Type:text/html;charset=GB2312
    Last-Modified
    Last-Modified實體報頭域用于指示資源的最后修改日期和時間。
    Expires
    Expires實體報頭域給出響應過期的日期和時間。為了讓代理服務器或瀏覽器在一段時間以后更新緩存中(再次
    訪問曾訪問過的頁面時,直接從緩存中加載,縮短響應時間和降低服務器負載)的頁面,我們可以使用Expires
    實體報頭域指定頁面過期的時間。eg:Expires:Thu,15 Sep 2006 16:23:12 GMT
    HTTP1.1的客戶端和緩存必須將其他非法的日期格式(包括0)看作已經(jīng)過期。eg:為了讓瀏覽器不要緩存頁
    面,我們也可以利用Expires實體報頭域,設置為0,jsp中程序如下:response.setDateHeader("Expires","0");

    五、利用telnet觀察http協(xié)議的通訊過程

        實驗目的及原理:
        利用MS的telnet工具,通過手動輸入http請求信息的方式,向服務器發(fā)出請求,服務器接收、解釋和接受請求
    后,會返回一個響應,該響應會在telnet窗口上顯示出來,從而從感性上加深對http協(xié)議的通訊過程的認識。

        實驗步驟:

    1、打開telnet
    1.1 打開telnet
    運行-->cmd-->telnet

    1.2 打開telnet回顯功能
    set localecho

    2、連接服務器并發(fā)送請求
    2.1 open
    www.guet.edu.cn 80  //注意端口號不能省略

        HEAD /index.asp HTTP/1.0
        Host:www.guet.edu.cn
       
       /*我們可以變換請求方法,請求桂林電子主頁內(nèi)容,輸入消息如下*/
        open
    www.guet.edu.cn 80
      
        GET /index.asp HTTP/1.0  //請求資源的內(nèi)容
        Host:www.guet.edu.cn  

    2.2 open www.sina.com.cn 80  //在命令提示符號下直接輸入telnet www.sina.com.cn 80
        HEAD /index.asp HTTP/1.0
        Host:www.sina.com.cn
     

    3 實驗結果:

    3.1 請求信息2.1得到的響應是:

    HTTP/1.1 200 OK                                              //請求成功
    Server: Microsoft-IIS/5.0                                    //web服務器
    Date: Thu,08 Mar 200707:17:51 GMT
    Connection: Keep-Alive                                
    Content-Length: 23330
    Content-Type: text/html
    Expries: Thu,08 Mar 2007 07:16:51 GMT
    Set-Cookie: ASPSESSIONIDQAQBQQQB=BEJCDGKADEDJKLKKAJEOIMMH; path=/
    Cache-control: private

    //資源內(nèi)容省略

    3.2 請求信息2.2得到的響應是:

    HTTP/1.0 404 Not Found       //請求失敗
    Date: Thu, 08 Mar 2007 07:50:50 GMT
    Server: Apache/2.0.54 <Unix>
    Last-Modified: Thu, 30 Nov 2006 11:35:41 GMT
    ETag: "6277a-415-e7c76980"
    Accept-Ranges: bytes
    X-Powered-By: mod_xlayout_jh/0.0.1vhs.markII.remix
    Vary: Accept-Encoding
    Content-Type: text/html
    X-Cache: MISS from zjm152-78.sina.com.cn
    Via: 1.0 zjm152-78.sina.com.cn:80<squid/2.6.STABLES-20061207>
    X-Cache: MISS from th-143.sina.com.cn
    Connection: close


    失去了跟主機的連接

    按任意鍵繼續(xù)...


    4 .注意事項:1、出現(xiàn)輸入錯誤,則請求不會成功。
              2、報頭域不分大小寫。
              3、更深一步了解HTTP協(xié)議,可以查看RFC2616,在
    http://www.letf.org/rfc上找到該文件。
              4、開發(fā)后臺程序必須掌握http協(xié)議

     

    六、HTTP協(xié)議相關技術補充

        1、基礎:
        高層協(xié)議有:文件傳輸協(xié)議FTP、電子郵件傳輸協(xié)議SMTP、域名系統(tǒng)服務DNS、網(wǎng)絡新聞傳輸協(xié)議NNTP和
    HTTP協(xié)議等
    中介由三種:代理(Proxy)、網(wǎng)關(Gateway)和通道(Tunnel),一個代理根據(jù)URI的絕對格式來接受請求,重寫全部
    或部分消息,通過 URI的標識把已格式化過的請求發(fā)送到服務器。網(wǎng)關是一個接收代理,作為一些其它服務
    器的上層,并且如果必須的話,可以把請求翻譯給下層的服務器協(xié)議。一 個通道作為不改變消息的兩個連接
    之間的中繼點。當通訊需要通過一個中介(例如:防火墻等)或者是中介不能識別消息的內(nèi)容時,通道經(jīng)常被使
    用。
         代理(Proxy):一個中間程序,它可以充當一個服務器,也可以充當一個客戶機,為其它客戶機建立請求。
    請求是通過可能的翻譯在內(nèi)部或經(jīng)過傳遞到其它的 服務器中。一個代理在發(fā)送請求信息之前,必須解釋并且
    如果可能重寫它。代理經(jīng)常作為通過防火墻的客戶機端的門戶,代理還可以作為一個幫助應用來通過協(xié)議處
    理沒有被用戶代理完成的請求。
    網(wǎng)關(Gateway):一個作為其它服務器中間媒介的服務器。與代理不同的是,網(wǎng)關接受請求就好象對被請求的
    資源來說它就是源服務器;發(fā)出請求的客戶機并沒有意識到它在同網(wǎng)關打交道。
      網(wǎng)關經(jīng)常作為通過防火墻的服務器端的門戶,網(wǎng)關還可以作為一個協(xié)議翻譯器以便存取那些存儲在非
    HTTP系統(tǒng)中的資源。
        通道(Tunnel):是作為兩個連接中繼的中介程序。一旦激活,通道便被認為不屬于HTTP通訊,盡管通道可能
    是被一個HTTP請求初始化的。當被中繼 的連接兩端關閉時,通道便消失。當一個門戶(Portal)必須存在或中介
    (Intermediary)不能解釋中繼的通訊時通道被經(jīng)常使用。


    2、協(xié)議分析的優(yōu)勢—HTTP分析器檢測網(wǎng)絡攻擊
    以模塊化的方式對高層協(xié)議進行分析處理,將是未來入侵檢測的方向。
    HTTP及其代理的常用端口80、3128和8080在network部分用port標簽進行了規(guī)定

    3、HTTP協(xié)議Content Lenth限制漏洞導致拒絕服務攻擊
    使用POST方法時,可以設置ContentLenth來定義需要傳送的數(shù)據(jù)長度,例如ContentLenth:999999999,在傳送完
    成前,內(nèi) 存不會釋放,攻擊者可以利用這個缺陷,連續(xù)向WEB服務器發(fā)送垃圾數(shù)據(jù)直至WEB服務器內(nèi)存耗
    盡。這種攻擊方法基本不會留下痕跡。
    http://www.cnpaf.net/Class/HTTP/0532918532667330.html


    4、利用HTTP協(xié)議的特性進行拒絕服務攻擊的一些構思
    服務器端忙于處理攻擊者偽造的TCP連接請求而無暇理睬客戶的正常請求(畢竟客戶端的正常請求比率非常之
    小),此時從正常客戶的角度看來,服務器失去響應,這種情況我們稱作:服務器端受到了SYNFlood攻擊(SYN洪水攻擊)。
    而Smurf、TearDrop等是利用ICMP報文來Flood和IP碎片攻擊的。本文用“正常連接”的方法來產(chǎn)生拒絕服務攻擊。
    19端口在早期已經(jīng)有人用來做Chargen攻擊了,即Chargen_Denial_of_Service,但是!他們用的方法是在兩臺
    Chargen 服務器之間產(chǎn)生UDP連接,讓服務器處理過多信息而DOWN掉,那么,干掉一臺WEB服務器的條件就
    必須有2個:1.有Chargen服務2.有HTTP 服務
    方法:攻擊者偽造源IP給N臺Chargen發(fā)送連接請求(Connect),Chargen接收到連接后就會返回每秒72字節(jié)的
    字符流(實際上根據(jù)網(wǎng)絡實際情況,這個速度更快)給服務器。


    5、Http指紋識別技術
       Http指紋識別的原理大致上也是相同的:記錄不同服務器對Http協(xié)議執(zhí)行中的微小差別進行識別.Http指紋識
    別比TCP/IP堆棧指紋識別復雜許 多,理由是定制Http服務器的配置文件、增加插件或組件使得更改Http的響應
    信息變的很容易,這樣使得識別變的困難;然而定制TCP/IP堆棧的行為 需要對核心層進行修改,所以就容易識
    別.
          要讓服務器返回不同的Banner信息的設置是很簡單的,象Apache這樣的開放源代碼的Http服務器,用戶可以在
    源代碼里修改Banner信息,然 后重起Http服務就生效了;對于沒有公開源代碼的Http服務器比如微軟的IIS或者
    是Netscape,可以在存放Banner信息的Dll文件中修 改,相關的文章有討論的,這里不再贅述,當然這樣的修改的效果
    還是不錯的.另外一種模糊Banner信息的方法是使用插件。
    常用測試請求:
    1:HEAD/Http/1.0發(fā)送基本的Http請求
    2:DELETE/Http/1.0發(fā)送那些不被允許的請求,比如Delete請求
    3:GET/Http/3.0發(fā)送一個非法版本的Http協(xié)議請求
    4:GET/JUNK/1.0發(fā)送一個不正確規(guī)格的Http協(xié)議請求
    Http指紋識別工具Httprint,它通過運用統(tǒng)計學原理,組合模糊的邏輯學技術,能很有效的確定Http服務器的類型.它
    可以被用來收集和分析不同Http服務器產(chǎn)生的簽名。


    6、其他:為了提高用戶使用瀏覽器時的性能,現(xiàn)代瀏覽器還支持并發(fā)的訪問方式,瀏覽一個網(wǎng)頁時同時建立
    多個連接,以迅速獲得一個網(wǎng)頁上的多個圖標,這樣能更快速完成整個網(wǎng)頁的傳輸。
    HTTP1.1中提供了這種持續(xù)連接的方式,而下一代HTTP協(xié)議:HTTP-NG更增加了有關會話控制、豐富的內(nèi)容
    協(xié)商等方式的支持,來提供
    更高效率的連接。

    posted @ 2009-02-17 17:26 葉澍成 閱讀(246) | 評論 (0)編輯 收藏
     

    數(shù)據(jù)對于輸入和輸出的操作耗時是非常嚴重的問題,如果把這個問題放入到網(wǎng)絡上去看待更甚是值得注意的一個問題了。假如結合基礎的OS知識我們也知道如果要減少這種I/O操作的耗時或者也可以說提升這種效率的話,最大的可能就是減少物理讀寫的次數(shù),而且盡可能做到主存數(shù)據(jù)的重讀性(操作系統(tǒng)也在加強說明更多減少抖動現(xiàn)象的產(chǎn)生)。

    java.nio包中我們可以直接來操作相對應的API了。可以讓java更加方便的直接控制和運用緩沖區(qū)。緩沖區(qū)有幾個需要了解的特定概念需要詳盡來解釋,才能更好的知道我們下面一些列需要針對的問題實質(zhì)。

    屬性

    容量(capacity):顧名思義就是表示緩沖區(qū)中可以保存多少數(shù)據(jù);

    極限(limit):緩沖區(qū)中的當前數(shù)據(jù)終結點。不過它是可以動態(tài)改變的,這樣做的好處也是充分利用重用性;

    位置(position):這個也好理解,其實就是指明下一個需要讀寫數(shù)據(jù)的位置。

    上面上個關系還可以具體用圖示的方式來表達整體概念,如下圖所示:


    在極限的時候就說到可以修改它,所以對于它的操作由以下方法:

    l         clear():首先把極限設置為容量,再者就是需要把位置設置為0

    l         flip():把極限設置為位置區(qū),再者就是需要把位置設置為0

    l         rewind():不改變極限,不過還是需要把位置設置為0

    最為最基礎的緩沖區(qū)ByteBuffer,它存放的數(shù)據(jù)單元是字節(jié)。首先要強調(diào)的是ByteBuffer沒有提供公開的構造方法,只是提供了兩個靜態(tài)的工廠方法。

    l         allocate(int capacity):返回一個ByteBuffer對象,參數(shù)表示緩沖區(qū)容量大小。

    l         allocateDirect (int capacity):返回一個ByteBuffer對象,參數(shù)也是一樣表示緩沖區(qū)容量大小。

    在這里需要注意的是在使用兩者的時候需要特別小心,allocateDirect和當前操作系統(tǒng)聯(lián)系的非常緊密,它牽涉到使用native method的方法,大家知道一旦本地方法就是需要考慮調(diào)用dll(動態(tài)鏈接庫)這個時候基本也就失去了JAVA語言的特性,言外之意對于耗資源非常大。所以如果考慮到當前使用的緩存區(qū)比較龐大而且是一個長期駐留使用的,這個時候可以考慮使用它。

    posted @ 2009-02-13 20:56 葉澍成 閱讀(244) | 評論 (0)編輯 收藏
         摘要:       在上篇blog中談到RMI的問世由來只是大致的把一些概念結構說明了下,自己靜靜想想要有好的說明干脆用代碼說明比較妥當也最為有說明性。事后自己倒騰了一個簡單的代碼DEMO。代碼中有個簡單的場景,比如你是屬于某地區(qū)醫(yī)保范圍內(nèi)的成員,到醫(yī)院看病,這個候醫(yī)院為了審核你的相關個人資料需要到醫(yī)保管理部門調(diào)閱信息,你只需要給出用戶名稱或者其他一個有...  閱讀全文
    posted @ 2009-02-03 21:56 葉澍成 閱讀(1787) | 評論 (3)編輯 收藏

     

     

    綜述

           Rmi自從JDK1.1就已經(jīng)出現(xiàn)了。而對于為什么在JAVA的世界里需要一個這樣 思想理念就需要看下:RMI問世由來。其實真正在國內(nèi)使用到它的比較少,不過在前些年比較火的EJB就是在它的基礎上進一步深化的。從本質(zhì)上來講RMI的興起正是為了設計分布式的客戶、服務器結構需求而應運而生的,而它的這種B/S結構思想能否和我們通常的JAVA編程更加貼切呢?言外之意就是能否讓這種分布式的狀態(tài)做到更加透明,作為開發(fā)人員只需要按照往常一樣開發(fā)JAVA應用程序一樣來開發(fā)分布式的結構。那現(xiàn)在的問題是如何來劃平這個鴻溝呢?首先我們來分析下在JAVA世界里它的一些特點因素:

    l         JAVA使用垃圾收集確定對象的生命周期。

    l         JAVA使用異常處理來報告運行期間的錯誤。這里就要和我們網(wǎng)絡通訊中的異常相聯(lián)系起來了。在B/S結構的網(wǎng)絡體系中我們的這種錯誤性是非常常見的。

    l         JAVA編寫的對象通過調(diào)用方法來調(diào)用。由于網(wǎng)絡通訊把我們的客戶與服務器之間阻隔開了。但是代理的一種方式可以很好的提供一種這樣的假象,讓開發(fā)人員或者使用者都感覺是在本地調(diào)用。

    l         JAVA允許一種高級的使用類加載器(CLassLoader)機制提供系統(tǒng)類路徑中沒有的類。這話什么意思?

    主要特點

    上面說到了分布式的方式和我們的JAVA中如何更好的劃平這個鴻溝,需要具備的特質(zhì)。

    那這里我們來看看我們所謂的RMI到底跟我們普通的JAVA(或者說JavaBean)存在一些什么樣的差異:

    l         RMI遠程異常(Remote Exception):在上面我們也提到了一個網(wǎng)絡通訊難免有一些無論是軟件級別的還是硬件級別的異常現(xiàn)象,有時候這些異常或許是一種無法預知的結果。讓我們開發(fā)人緣如何來回溯這種異常信息,這個是我們開發(fā)人員要關心的。因此在調(diào)用遠程對象的方法中我們必須在遠程接口中(接口是一種規(guī)范的標準行為)所以在調(diào)用的這個方法體上需要簽名注明:java.rmi,RemoteException.。這也就注明了此方法是需要調(diào)用遠程對象的。

    l         值傳遞 :當把對象作為參數(shù)傳遞給一個普通的JAVA對象方法調(diào)用時,只是傳遞該對象的引用。請注意這里談到的是對象的“引用”一詞,如果在修改該參數(shù)的時候,是直接修改原始對象。它并不是所謂的一個對象的備份或者說拷貝(說白了就是在本JVM內(nèi)存中的對象)。但是如果說使用的是RMI對象,則完全是拷貝的。這與普通對象有著鮮明的對比。也正是由于這種拷貝的資源消耗造就了下面要說到的性能缺失了。

    l         調(diào)用開銷:凡是經(jīng)過網(wǎng)絡通訊理論上來說都是一種資源的消耗。它需要通過編組與反編組方式不斷解析類對象。而且RMI本身也是一種需要返回值的一個過程定義。

    l         安全性:一談到網(wǎng)絡通訊勢必會說到如何保證安全的進行。

     

    概念定義

    在開始進行原理梳理之前我們需要定義清楚幾個名詞。對于這些名詞的理解影響到后的深入進行。

    1.         Stub(存根,有些書上也翻譯成:樁基在EJB的相關書籍中尤為體現(xiàn)這個意思):

    這里舉例說明這個概念起(或許不夠恰當)。例如大家因公出差后,都有存在一些報銷的發(fā)票或者說小票。對于你當前手頭所拿到的發(fā)票并不是一個唯一的,它同時還在你發(fā)生消費的地點有一個復印件,而這個復印件就是所謂的存根。但是這個存根上并沒有很多明細的描述,只是有一個大概的金額定義。它把很多的細節(jié)費用都忽略了。所以這個也是我們說的存根定義。而在我們RMI的存根定義就是使用了這樣一個理解:在與遠程發(fā)生通訊調(diào)用時,把通訊調(diào)用的所有細節(jié)都通過對象的封裝形式給隱藏在后端。這本身就符合OOAD的意思理念。而暴露出來的就是我們的接口方式,而這種接口方式又和服務器的對象具有相同的接口(這里就和我們前面舉例說的報銷單據(jù)聯(lián)系上了,報銷單據(jù)的存根不知道會有一個什么形式發(fā)生具體問題,而你手執(zhí)的發(fā)票具體就需要到貴公司去報銷費用,而這里的公司財務處就是所謂的服務器端,它才是真正干實質(zhì)性問題的。)因此作為開發(fā)人員只需要把精力集中在業(yè)務問題的解決上,而不需要考慮復雜的分布式計算。所有這些問題都交給RMI去一一處理。

    2.         Skeleton(一些書翻譯叫骨架,也叫結構體):它的內(nèi)部就是真正封裝了一個類的形成調(diào)用體現(xiàn)機制。包括我們熟知的ServerSocket創(chuàng)建、接受、監(jiān)聽、處理等。

    3.         Mashalling(編組):在內(nèi)存中的對象轉換成字節(jié)流,以便能夠通過網(wǎng)絡連接傳輸。

    4.         Unmashalling(反編組):在內(nèi)存中把字節(jié)流轉換成對象,以便本地化調(diào)用。

    5.         Serialization(序列化):編組中使用到的技術叫序列化。

    6.         Deserializationg(反序列化):反編組中使用到的技術叫反序列化。

     

    客戶端

           既然我們知道stub主要是以接口的方式來暴露體現(xiàn),而stub主要 也是以代理的方式來具體實施。那在RMI中的這種接口有哪些特性呢?(Remote Interface

    1)        必須擴展(extendsjava.rmi.Remote接口,因為遠程接口并不包含任何一個方法,而是作為一個標記出現(xiàn),它就是需要告訴JVMRunTime的時候哪些是常規(guī)對象,哪些屬于遠程對象。通過這種標識的定義能讓JVM了解類中哪些方法需要編組,通過了編組的方式才能通過網(wǎng)絡序列化的調(diào)用;

    2)        接口必須為public(公共),它的好處不言而喻的——能夠方便的讓所有人員來調(diào)用。

    3)        接口方法還需要以異常拋出(例如:RemoteException),至于它的用處我們在前面也提到這里就不再復述;

    4)        在調(diào)用一個遠程對象期間(運行期間),方法的參數(shù)和返回值都要必須是可序列化的。至于為什么需要這么做?這里的緣由不用多說大家也應該清楚了解。

    服務端

           既然我們知道stub所做的事情是一個簡單的代理轉發(fā)動作,那我們真正要做的對象就在服務端來做了。對于使用簡單的RMI我們直接去指定,但是往往一旦使用了RMI對象就存在非常多的遠程方法調(diào)用,這個時候服務器端對于這么多的調(diào)用如何來判別或者說識別呢?這里就要說到的是對于RMI實現(xiàn)它會創(chuàng)建一個標識符,以便以后的stub可以調(diào)用轉發(fā)給服務器對象使用了,而這種方式我們通常叫服務器RMI的注冊機制。言外之意就是讓服務器端的對象注冊在RMI機制中,然后可以導出讓今后的stub按需來調(diào)用。那它又是如何做到這種方式的呢?對于RMI來說有兩種方式可以達到這種效果:

    a)         直接使用UnicastRemoteObject的靜態(tài)方法:exportObject

    b)        繼承UnicastRemoteObject類則缺省的構造函數(shù)exportObject

    現(xiàn)在大家又會問他們之間又有什么區(qū)別呢?我該使用哪種方式來做呢,這不是很難做抉擇嗎?從一般應用場景來說區(qū)別并不是很大,但是,這里說了“但是”哦,呵呵。大家知道繼承的方式是把父類所具備的所有特質(zhì)都可以完好無損的繼承到子類中而對于類的總老大:Object來說里面有:equals()hashCode()toString()等方法。這是個什么概念呢?意思就是說如果對于本地化的調(diào)用,他們兩個的方法(a,b)基本區(qū)別不是很大。但是我們這里強調(diào)的RMI如果是一種分布式的特定場景,具備使用哈希表這種特性就顯得尤為重要了。

    剛才說了服務端采用什么方法行為導出對象的。那現(xiàn)在導出后的對象又對應會發(fā)生什么情況呢?

    首先被導出的對象被分配一個標識符,這個標識符被保存為:java.rmi.server.ObjID對象中并被放到一個對象列表中或者一個映射中。而這里的ID是一個關鍵字,而遠程對象則是它的一個值(說到這大家有沒有覺得它原理非常像HashMap的特質(zhì)呢?沒錯,其實就是使用了它的特性),這樣它就可以很好的和前面創(chuàng)建的stub溝通。如果調(diào)用了靜態(tài)方法UnicastRemoteObject.export(Remote …)RMI就會選擇任意一個端口號,但這只是第一調(diào)用發(fā)生在隨后的exportObject每次調(diào)用都把遠程對象導出到該遠程對象第一被導出時使用的端口號。這樣就不會產(chǎn)生混亂,會把先前一一導出的對象全部放入到列表中。當然如果采用的是指定端口的,則按照對應顯示的調(diào)用方式使用。這里稍作強調(diào)的是一個端口可以導出任意數(shù)目的對象。

    (待續(xù)……
    posted @ 2009-02-02 12:04 葉澍成 閱讀(3402) | 評論 (3)編輯 收藏
        大家都知道對于互聯(lián)網(wǎng)的世界網(wǎng)絡通訊是其本質(zhì)特征。而對于一個分布式式計算來說更是如此。在它的環(huán)境中使用了客戶/服務器結構特點,使用的一個核心技術就是網(wǎng)絡通訊層。在最早的OSI的概念基礎上,建立了完善具體協(xié)議層。而客戶想要能夠與位于其他物理層主機上的服務器通訊,需要能夠想服務器發(fā)送數(shù)據(jù),然后以某種方式獲得響應。這當中就牽涉到我們熟悉的協(xié)議層面了,在這里就不再復述這些協(xié)議概念了。對于網(wǎng)絡通訊來說我們所要了解的是最為常用的就是兩種連接方式:無連接協(xié)議(UDP)、面向連接協(xié)議(TCP/IP)。

           多數(shù)網(wǎng)絡編程庫中(以JAVA為主來說明),在JAVA平臺中一樣的提供了這些元素。而作為面向連接協(xié)議來說使用的是套接字(Socket)進行了更進一步的抽象描述。一般我們在JAVA的網(wǎng)絡編程中都覺得在使用套接字這塊相對方便,它不需要你去更多的了解操作系統(tǒng)的細節(jié)以及硬件的傳遞處理方式。TCP/IP的所有細節(jié)之處都得到了套接字的封裝使用,讓程序員關注到業(yè)務層面的處理。

           對象是一種抽象思維物質(zhì),對于計算機來說它只對數(shù)字電路的存儲方式能夠加以識別而且在網(wǎng)絡傳輸當中也是一種信號量,而這一切只有使用字節(jié)流方式傳輸才是真正需要做到的。所以在本地主機與遠程服務器的通訊傳輸就在對象與字節(jié)流之間不斷相互轉化才是我們真正需要的人性物質(zhì)與機器所需要的。(有點墨跡了,切入整體)總體來說就是需要兩種方式來認定這種傳輸行為:編組(Marshalling)與反編組(Unmarshalling)。而這一切的手段方式才是通過:序列化(Serialiazable)與反序列化的方式不斷完成。如下圖所示:

     

    圖:對象到字節(jié)再到對象的轉換

           對于數(shù)據(jù)的傳輸本質(zhì)就是上圖說明的。那我們一般是如何使用套接字來構造我們這一行為呢?對于這里強調(diào)的主要是一種大致方法說明,所以只能以部分代碼來說明客戶端怎么來發(fā)送這個請求。

    Socket socket=new Socket("http://www.wgh.com.cn",8888);

        OutputStream out=socket.getOutputStream();

        ObjectOutputStream obj=new ObjectOutputStream(out);

        obj.writeObject(object);

        InputStream in=socket.getInputStream();

        ObjectInputStream objIn=new ObjectInputStream(in);

        Object objFoo=(Object)objIn.readObject();

        //todo 這里就是具體進行操作的相關傳值參數(shù)處理了

        obj.close();

        objIn.close();

        socket.close();

    而作為服務器的接收方則把以上數(shù)據(jù)做一個逆轉相反處理就可以。即服務器需要讀取發(fā)送過來的對象數(shù)據(jù),最終得到結果。現(xiàn)在假設還是一個甚至更多這樣的對象處理,我們又要處理和以上代碼差不多的過程。好,到這里我們可曾想到難道沒有一種辦法把這些過多的重復過程做一個通用的方式來提供嗎?我如果不想去做這些繁雜的對象處理可以嗎?比如,我想直接得到:

    //其中clientObjectji就是從客戶端傳輸過來的副本;

    MyObject myObject=server.getFoo(clientObject);

    這樣就能讓我們把底層的那些龐雜數(shù)據(jù)轉換能夠透明封裝起來呢?既然這個問題一經(jīng)提出,那就意味著肯定有很多類似的需求。技術永遠都是在需求的提出應運而生的。上面提出的需求就是我們要討論的,既然我們想把一些套接字的重復處理過程來個封裝清理,那需要面對的問題是什么呢?

    1.      能夠把所有的相同處理過程全部都移入到服務端呢?

    2.      對于客戶端來說能否只預留接口行為呢?

    3.      把過多的復雜處理過程完善的做個封裝?

    4.      如果以上過程能夠形成,那客戶端又是如何辦到可以連接到服務器端的組件行為呢?

    既然能夠把遇到的問題提出然后總結出來也就意味著我們需要去解決它。不知道是否還

    記得設計模式中有一個叫:代理模式?沒錯,就是這個代理模式開始我們的描述。代理是一個實現(xiàn)給定接口的對象,但是不直接去執(zhí)行代碼結果,而是代表其他一些對象執(zhí)行實際計算的對象。怎么理解這個話呢?舉例說,如今很多城市都有火車票或者飛機票的代售點,這里的代售點其實就是采用了一種代理機制。我們想買某天的火車或者飛機票有時候并不需要到火車站或者飛機票的總點去購買票,而是找一個你最近的代售點去購買。代售點就是起到一個中間橋梁的作用,至于買票人員無需關心他們?nèi)绾稳ビ嗁彛@些具體的動作都由他們內(nèi)部去處理,你只關心最終是否有你需要的票就行。知道這個原理接下來就好理解很多了,我們最好以類圖的方式來說明這個代理的機制,如圖所示:


     

        到這里如果還覺得抽象,沒關系接下來我以更加貼切的實例來結合類圖的方式給出對應的參照說明。假如我們把上面的proxy模式再做個深入的探討剖析(結合上面說的客戶端發(fā)送參數(shù)作為請求和提出的問題綜述)。大家都知道一個接口是能夠在安全甚至在擴展上能夠幫助我們非常大的功能。作為客戶端最為希望的就是只關心我們需要的參數(shù)(或者變量)、返回值,而它如何而來,做了哪些具體工作這些都不是客戶端關心的。Ok,現(xiàn)在結合我們說的接口方式,確實可以解決這個問題(接口的簡單化,沒有具體實現(xiàn)),但是你可能會問:

    1.      既然接口如此簡單,那參數(shù)又是如何傳遞過去的呢?

    2.      服務端又如何知道我要的是什么呢?

    帶著問題我們來解決這個問題,當然也是大家所關心的問題了。現(xiàn)在開始要對于上面的proxy模式做個深入剖析了。我們先來看一個proxy模式演變的過程的圖示:

     

    圖:RMI核心結構

        我們可以從圖示看出從傳統(tǒng)的proxy模式變化到一個變化的結構有什么不同呢?對于先前我們提出的兩個問題就可以很好的做出解釋了:

    n         既然接口如此簡單,那參數(shù)又是如何傳遞過去的呢?

    A:既然是對客戶端只開接口暴露,那么我們是就需要一個后臺的socket來傳輸接口中已經(jīng)定義好的參數(shù),通過參數(shù)的編組(序列化)方式請求到遠程服務上去響應處理。這當中要求定義到對方服務的服務名稱和端口號。(這里也就是我們最先提到的那段代碼了)

    n         服務端又如何知道我要的是什么呢?

    A:ok,既然客戶端是通過socket來發(fā)送數(shù)據(jù),那勢必一定需要ServerSocket來做這個響應的接收處理了。問題是傳過來的參數(shù)如何與我們的業(yè)務實現(xiàn)類關聯(lián)上呢?所以這個也就是skeleton的職責所在了,在skeleton的封裝處理中(啟動中就把響應實現(xiàn)類給嵌入,聚合實現(xiàn)類),然后通過類轉換處理和匹配處理來得到需要響應的結果了。

       

        本來說到這想大概有個收尾,但是總覺得還沒有把一些問題說透徹。索性想再深入寫寫。
        從套接字衍生到RMI代碼思路

    posted @ 2009-02-02 11:57 葉澍成 閱讀(3584) | 評論 (1)編輯 收藏

     

    進程的創(chuàng)建

    進程本身是一個動態(tài)的實體,所以它本身在運行期間也通過創(chuàng)建進程系統(tǒng)調(diào)用,并且可以創(chuàng)建多個新進程,對于這句話我同樣使用圖解的方式來做個簡單說明:


    當一個進程創(chuàng)建一個新進程時,會存在兩種可能的方式執(zhí)行:

     

    1.         父進程(繼續(xù)執(zhí)行)和子進程并行的執(zhí)行;

    2.         父進程等待部分或者全部子進程終止執(zhí)行;

    而新進程的地址空間也存在兩種可能:

    1.         子進程是父進程的一個COPY了;

    2.         載入一個程序來運行;

    到這里就有點感覺Erlang的進程思想端倪了(開始我咋看感覺有點象,但是越深入后就覺得確實就是到這個思想才形成了Erlang程序語言的意義本質(zhì),個人揣度啊,哈哈)既然有那么點思想的感覺,那我們就開始進入探討階段了。也正是下面即將要講到的問題才印證一句話:技術的本質(zhì)還存留在簡單事物之上(個人總結,哈哈)。

    進程間通信

    進程間通信有兩種本質(zhì)的方式:

    1.         共享緩沖區(qū)提供通信;

    2.         消息傳遞;

    大家有沒有認真看到上面的四個字“消息傳遞”,對沒錯就是消息傳遞!那這里我就感覺是否就是這里和Erlang的語言所談到的消息傳遞呢?盡管一個處于操作系統(tǒng)級別,而另一處于語言級別,但是初看給我的感覺是原理是否一致呢?呵呵,那就讓我們來看看OS級別的進程間通信本質(zhì)起了。

    消息傳遞系統(tǒng)

    消息系統(tǒng)的功能是允許進程與其他進程之間通訊不必借助共享數(shù)據(jù),他們各自獨立。而這里要說到一個概念,什么是IPC?

    如果我們先不看它定義,而了解具體做法,看是一個什么效果。

     

    到這里為止是不是感覺又和我們說的Erlang非常類似呢?真的沒錯。。。那就繼續(xù)往下看看它到底如何而做了。有以下幾種方法實現(xiàn)和Send/Receive操作的方法:

    u       直接或者間接通信

    u       對稱或不對稱通信

    u       自動或手動緩沖

    u       發(fā)送copy或者引用

    u       定長消息或者變長消息

    為了更好的說明上面各自的特征是如何引入和體現(xiàn)的,使用兩個典型的進程p,q作為兩個進程之間的通訊來加以演示。

    直接通信:

           這里就是兩個非常赤裸的而且是非常利索的通信了:


    u       send(P,message) P發(fā)送一個消息給進程Q

     

    u       receive(Q,message) 從進程Q中接收一個消息

    特點

    1.         每對需要通信的進程之間自動建立一條鏈路,進程只需要知道彼此的進程標識符(Pid);

    2.         一個連接就只連接到這兩個進程;

    因此這種機制在尋址上是對稱的;發(fā)送者與接收者進程都必須要指明通信一方。不過這里要談到它的一種特例:發(fā)送者需要知道接收者,但是接收者不需要知道發(fā)送者,其原語定義為:

    SendP,Message 發(fā)送一個消息給P

    Receiveid,Message)從任意進程中接受一個消息;

    由于通信是一種交互行為,所以一般情況來說我們有發(fā)送自然希望存在一個回復的交互動作,而像這種特例就無法知道它的這種情形,因此這種情況也是我們不希望所見的。

    間接通信

           間接通信中消息發(fā)送和接收則是通過郵箱(實際中更多的是端口方式,這里為了更好理解我們比作郵箱方式)進行的。若把郵箱看成一個對象,進程就可以把消息郵寄(放置)在其中,顯而易見既然能夠放入自然也就可以從郵箱中取出了。而每一個郵箱都有一個唯一的地址(標識符),進程可以通過不同的郵箱和其它進程通信了。兩個進程只有共享一個郵箱才可以進程通信。因此基本構建和原語定義圖示如下:

    SendA,M):發(fā)送消息(Message)給郵箱A

    ReceiveA,M):從郵箱A接收到消息(M);


    特點:

     

    1.         只有在兩個進程間有一個共享郵件箱下才能兩者建立一個連接;

    2.         一條鏈路可以連接兩個或者更多的進程;

    3.         每對通信進程之間可以同時存在多個不同的鏈路,而且每條鏈路對應一個郵箱;

    那我們來看看直接通信和間接通信最大的區(qū)別是什么?沒錯,其實間接通信存在中間一個共享的郵箱,而正是這種方式才在以后的應用中得到廣泛利用。這里就聯(lián)想到Erlang語法的:Pid!M是否感覺很相似(注:M消息通知標示符為Pid的進程操作事件,而且具體在接受中也存在得到一個receive來獲取消化了),在我看來它就是典型使用到了這個原理機制。這里來看一道例題:假如有兩個進程P1,P2P3都存在共享郵箱A,而且P2P3正是從A中接收。那么誰 接收到P1發(fā)送來的消息呢?圖示:


    這里我們就需要具體討論問題的實質(zhì)了:

     

    對照上面說講到的間接通信特點,我們知道一條鏈路最多連接兩個進程,同時最多允許任意選擇進程來接收消息(現(xiàn)在這個例子中只存在P2,P3),所以他們兩者只允許單獨接收消息而不是同時接收消息。至于消息會發(fā)送給誰,這就需要系統(tǒng)本身來確定接收者了。因此,一個郵箱可能為一個進程或者操作系統(tǒng)所有,不難看出這個例子存在以下情況:


    一旦擁有郵箱A的進程終止時,郵箱也就同時要消失,隨后向該郵箱發(fā)送消息的進程就會被告知郵箱不存在。這里需要強調(diào)的是作為操作系統(tǒng)本身來說擁有一個郵箱是獨立的不依賴于任何進程。所以操作系統(tǒng)有必要提供一種機制允許一個進程來專門做這個工作了,那是什么工作呢?具體有以下特征:

    u       創(chuàng)建一個新郵箱;

    u       通過這個郵箱發(fā)送和接收信息;

    u       刪除一個郵箱;

    接下來就開始單獨討論所謂的這個“郵箱”的單獨機制能夠引發(fā)的一些線索了。

    進程同步

           通過消息傳送來進程通信,這個是它本質(zhì)所在。但是消息傳送可能有阻塞或者無阻塞——同步和異步。所以這里就存在對于發(fā)送者和接收者的阻塞或無阻塞現(xiàn)象的討論了,對于他們有一下特點:

    1.         發(fā)送進程阻塞:發(fā)送進程被阻塞,直到接收進程接收消息;

    2.         發(fā)送進程無阻塞:發(fā)送進程發(fā)送消息并且立刻恢復執(zhí)行;

    3.         接收進程阻塞:接收進程被阻塞,知道一個消息為有效;

    4.         接收進程無阻塞:接收進程獲取一個有效或空消息;

    以上的方式還可以組合形成。

    既然有阻塞和無阻塞現(xiàn)象,那立刻可以聯(lián)想到我們的郵箱擴展成一種管道方式呢?沒錯這里就需要講解下面的定義形成。

    緩沖

           這里只想對于三個定義了解:

    u       零長度:無緩沖消息系統(tǒng);

    u       限定長度:

    自動緩沖

    u       無限長度:

    這里單獨把限定長度和無限長度提取出來定義:

    限定長度:消息隊列中存在N個消息,發(fā)送者在發(fā)送未滿的隊列中無阻塞。一旦隊列滿則阻塞直到出現(xiàn)空閑空間。

    無線長度:消息隊列有無限個消息,也不會阻塞發(fā)送者。

           下面我們就要通過這些概念擴展到程序開發(fā)中經(jīng)常會遇到的實例。

    (待續(xù)。。。。。。)

    posted @ 2008-12-14 12:42 葉澍成 閱讀(1216) | 評論 (0)編輯 收藏
     

    在學習Erlang程序過程中,總覺得對于進程還是沒有很好的把握。所以自己對于進程的再次提及讓我不得不重溫操作系統(tǒng)這門看似抽象的課程了。但是總覺得如果單一講解進程或許略顯抽象不夠理解,自己就想把某些經(jīng)驗和知識片段有個很好的系統(tǒng)聯(lián)系起來,我想這樣可以讓自己更好加強記憶理解。長話短說,我們進入主題,既然在Erlang的學習中始終圍繞著進程一詞來深入研究,我們就從進程這個話題談起。

    進程概念

    1.         進程是運行中的程序。這里我們就可以稍微延伸下以便幫助我們記憶理解了:

    在這里我們只要抓住“運動”詞匯,就不然發(fā)現(xiàn)進程是個動態(tài)的實體,與之對應的是我們常說的程序,程序而是一個靜態(tài)實體了。(bw:這里突然想到以前對于認識EJB2中也有一個對應的概念就是EntityBeanSessionBean,它們與之對應的就是一個典型的名詞和動詞概念了,哈哈啰嗦了,轉回正題)。那我們會想是什么來體現(xiàn)我們說的進程為一個動態(tài)實體呢?立刻會聯(lián)系產(chǎn)生接下來的一個特點了。

    2.         進程不僅僅是程序代碼,它包含了當前狀態(tài)。而這種狀態(tài)由兩個方面來表示:

    u       程序計數(shù)器(program counter

    u       cpu中的寄存器(registers

    就是由于進程中有程序計數(shù)器指明下一條要執(zhí)行的指令而且擁有一組相關的資源。這里又要繼續(xù)反問:到底是一個什么資源呢?那就要看下面會講到的PCB的概念了。

    3.         進程還包含進程棧(process stack)。

    例如:方法參數(shù)(method parameters;返回地址與本地變量等

    4.         兩個進程可能會關聯(lián)到同樣的程序,剛才我們說到了所謂程序是一個靜態(tài)實體。顧名思義就是兩個進程允許使用同樣的代碼段,只是在參數(shù)不同而已。但是仍然認為這兩個進程是獨立執(zhí)行的序列。例如:幾個用戶可能會同事運行主程序的不同拷貝一樣。

    進程狀態(tài)

    注:上面就是一個完整的進程狀態(tài)圖了。這里沒有必要多說什么了,圖表給了我們一個輪廓。

    進程控制塊(PCB

    這里就是我們要講到的進程控制塊了,對于操作系統(tǒng)都是需要通過PCB來表示進程的。有些資料上也稱作為:任務控制塊。對于PCB的整體描述我們還是以圖表的方式來說明(這也是本人最喜歡也覺得最直觀的一種理解方式了):

    Pointer(指針)

    Process state(進程狀態(tài))

    Process number(進程序列號)

    Program counter(程序計數(shù)器)

    Register(寄存器)

    Memory limits(主存中受限說明)

    List of open files

    。。。(這里其實還有很多,就不再一一列舉了)

    PCB描述圖

    既然是操作系統(tǒng)都需要通過進程控制塊來調(diào)用進程過程,那我們在這里舉例說明下如果有兩個進程結合PCB是如何在CPU之中切換使用進程的。為了更加清晰了解它們的過程,還是老規(guī)矩使用一個圖表來展示兩個進程分別為P1,P2怎么運行的。

    (這個圖在自己的筆記本上已經(jīng)用筆畫出來了,可就是找不到一個好的工具,暫時放置在這,待續(xù)畫圖了)

    進程調(diào)度

    由于我們目前接觸的其實多半都是以分時系統(tǒng)為主的,其目標也是為了在進程之間頻繁轉換cpu以便由于用戶與運行的程序來交互。

    1.         進程進入系統(tǒng)后,都被放在隊列中了,我們稱這個隊列為:作業(yè)隊列(也有些書上不是這么稱呼的),所有的進程都在這個其中。

    2.         處于就緒進程都被保留在一個列表中——就緒列表

    3.         一旦進程獲得了CPU并且執(zhí)行,就可能發(fā)生一下某個事件存在:

    u       進程可能發(fā)出一個I/O請求,然后被放置在I/O隊列中;

    u       進程也可能創(chuàng)建一個新的子進程并且等待它終止;

    u       有時候發(fā)生一個中斷,導致進程強行從CPU里移除并返回就緒隊列;

    調(diào)度程序

    這里需要了解兩個主要的概念:

    1.         長程調(diào)度(操作系統(tǒng)調(diào)度程序):從一個池中選擇進程并其載入內(nèi)存中。

    注:咋看不是很了解,可能這里需要了解虛擬存儲過程對于這個概念就比較好理解。這里大致說明下。由于過去我們所使用的內(nèi)存(主存儲器)空間非常有限,在搶占的進程志愿中如果都想放入內(nèi)存中顯然是不夠科學,而對于我們的后備動力輔助存儲器——磁盤(這里我們說是硬盤,當然也有3.5英寸的小磁盤了,呵呵)來說,就可以充分考慮到使用它來做個過度動態(tài)的存儲器。所以這樣一來我們就不需要把一個進程中所有的信息都裝載到內(nèi)存中,而是在需要是再來考慮換入;而與之相反的就是不需要時就換出了(bw這里的換入/換出如果比較頻繁也就證明我們的內(nèi)耗比較嚴重,一般稱作這個叫:抖動現(xiàn)象。大家有時候感興趣的可以觀察我們主機的硬盤燈如果在頻繁的閃動就表示資源在不斷換入換出了,呵呵)。而這也就是虛擬存儲的一個本質(zhì)過程,當然中間需要通過邏輯轉換表來過度,在這里我們就不再具體復述了。有興趣的可以去看看相關OS方面的書籍。

    2.         近程調(diào)度(CPU調(diào)度程序):從這些進程中選擇就緒進程并為其某個分配CPU

    注:說白了這里就是我們的cpu直接通過緩沖通道來調(diào)用就緒的程序進入運行。

    上下文轉換

    前面我們也談到了進程是在CPU中來回切換運行的,既然是一種來回切換運行那勢必需要讓CPU知道我們切換到下一個狀態(tài)執(zhí)行的地址或者說切入點在哪了。這個就是為什么需要一個程序計數(shù)器的功效了。那我們把這種來回切換狀態(tài),同時需要保存當前運行進程狀態(tài)信息給記錄下來以便下一次能夠定位到的方式稱作是——上下文轉換。

    就上面的這樣一次轉換表述在一定程度上需要耗的硬件資源非常大(這里又要我強調(diào)下,更多的信息需要你還了解一些計算機組成體系結構了。希望有時間自己也總結一篇關于一個簡單程序在體系中運行過程)。因此上下文轉換在很大程度上就取決于硬件的支持了。

    接下來要講到的應該是最核心的也是對以后我們無論是寫程序好還是學習一門新語言好,都需要很好理解的部分了。在這里也盡可能表述清楚。
    (待續(xù)......)

    posted @ 2008-12-14 00:25 葉澍成 閱讀(1424) | 評論 (0)編輯 收藏
     

    云計算應該所具備的特質(zhì)如下:

    1.         高負載

    2.         正常運轉

    3.         容錯性

    4.         分布式

    5.         容易伸縮

    Erlang(讀音:['?:læ?]厄蘭,中文意思為:占線小時(話務負載單位))正是由于它屬于開放的電信業(yè)務平臺,也就不難理解它的意義了。幾乎完全具備以上特質(zhì),而且它也是典型的函數(shù)式語言。和我們OOP的思想有著截然不同的概念。在以下的學習過程中主要還是以《Erlang程序設計》這本書作為一個學習的依據(jù)。

    原子

    定義:在Erlang中原子用來表示不同的非數(shù)字常量值。這里說白了其實就是一種常量的定義。Erlang中原子是全局有效的,不需要像以前c/c++那樣通過宏來定義或者包含文件。在定義原子的時候只需要注意以下一些特點就可以:

    1.         一般情況原子是以一串以小寫字母開頭,后面有數(shù)字、字母、下劃線、郵件符號(@);

    2.         使用單引號引用起來的字符也屬于原子,例如’Monday’

    3.         一個原子的值就是原子本身;

    元組(tuple

    定義:首先它是Erlang中具有特質(zhì)的一個定義,如果說把它和我們java中的一個JavaBean來類比可能稍顯類似,書上引用的是c語言數(shù)據(jù)結構來解說元組的結構,盡管非強淺顯能看懂。但是作為一個java程序員我覺得采用自己熟悉的語言結構來對比,學習效果更佳吧(對于記憶有很大幫助)。

    比如我們一般對于JavaBean的定義是如下結構:

    public class Person {

        private String name;

        private int height;

        private int footSize;

        private String eyeColor;

        // get/set...

    }

    那在我們引用定義的時候就可以直接:

    Person person1=new Person();

    person1.setName("yeshucheng");

    person1.setHeight(111);

    person1.setFootSize(40);

    person1.setEyeColor("black");

    ......

    與之相對應的是我們使用Erlang來定義了,對于Erlang的定義就截然和c/c++或者java有著明顯不同,相對于更加精煉明了:(這里我直接使用書上說的所謂二元組)

    Person={person,{name,yeshucheng},{height,111},{footsize,40},{eyecolor,black}}.

    沒錯,就是這么直截了當?shù)膩矶x,甚至賦值(嚴格說Erlang不能這么說,但是為了好記憶可以這么理解)

    對于以上的定義這里要說明注意的地方:

    1.      定義元組,元組中字段沒有名字,通常可以使用一個原子作為元組的第一元素來標明(請注意這里花括號內(nèi)第一原子都是解釋逗號后面一個說明),這個元組所能代表的含義就是上面列出的程序定義了。

    2.      創(chuàng)建元組,在聲明元組的同時其實已經(jīng)創(chuàng)建了元組,這個也是Erlang的一大特點之一了。如果不再使用,也隨之銷毀。Erlang使用的垃圾搜集器去收回沒有使用的內(nèi)存。

    如:F={firstName,wan}

    L={lastName,andy}

    P={person,F,L}//這里就應對我們第一條說明的一樣第一個名稱表示就是后面所有逗號的整體列舉,如果在Erlang環(huán)境中對于上面寫完后,直接敲回車(語句結束后存在”.”這里稍微注意下)就會得到以下結果,正好印證我們所說明這這個問題了

    =={persong,{firstName,wan},{lastName,andy}}.

    如果在創(chuàng)建過程中存在一個未定義的變量,則程序編譯就會產(chǎn)生錯誤。

    3.      提取元組的字段值,剛才我們在程序中有定義一個Person的元組而且也設置值了,現(xiàn)在如果我們想得到或者說提取我們的值,那需要如何而做呢?首先我們采用基本的元組方式來試著看看如下:

    1>    Point={point,10,45}.

    2>    {point,X,Y}=Point.

    3>    X.

    10

    4>    Y.

    45

    注明:這里又再次強調(diào)下point逗號后面的都是為他而說明的。

    1>Person={person,{name,yeshucheng},{height,111},{footsize,40},{eyecolor,black}}.

    2>{_,{_,Who},{_,_},{_,_},{_,_}}=Person.

    3>Who.

    yeshucheng

    說明下,如果上面想得到的是值,那么位置響應對號入座然后Who換成What就成(我開始也犯錯誤,編譯立馬出錯,后來想想用過一個What試試,果然正確,呵呵)。

    列表

    定義:列表第一個元素稱為列表的頭(head,后部分稱為列表尾(tail),一般[H|T]來標示列表了。

    注:列表的頭可以是任何東西,但是列表的尾通常還是一個列表。

    至于具體的細節(jié)問題還是需要找找相關文檔看下為好,它的概念牽涉到后面的非常多的定義了。

    posted @ 2008-12-09 10:20 葉澍成 閱讀(1014) | 評論 (0)編輯 收藏
     

    邊緣技術人員,這里是個人的一個定義闡述而已,所有的售前售后咨詢師、維護人員、培訓講師等都包含在這個頭銜中。咋看感覺自己對這個頭銜有失偏頗的定論,其實不然我對這個行業(yè)的人員還是挺敬佩的。為什么這么說呢?因為他們當中多數(shù)是在和我們的客戶一線打交道,客戶的喜怒哀樂,喜形于色都能第一時間映入他們的腦海,他們需要學會最大的程度承受,即使受到某種輕視或者詆毀的狀態(tài)都需要忍受,同時還要很快的去更好處理當時的尷尬場景。某種程度來說他們類似一些公司業(yè)務跑單人員,但是他們有更多的技術背景同時這里面也有很多的牛人。這類人員所具備的素養(yǎng)更加強調(diào)人性的一面,也正是由于接觸不同社會個階層人員的廣泛,相對于那些整天坐在辦公室電腦前敲代碼的程序員來說是截然不同的天地。程序員的思維縝密這個都是毋庸置疑的,但是也正是這種思維方式讓他們?nèi)菀紫萑胍?guī)則死板的世界,甚至有些人員會鉆牛角尖(你可別不承認哦?或許就是你自己了,哈哈)而對于這里的邊緣技術人員來說如果遇到同樣的一件事情很有可能他處理的方式會有很大不同,他的某種圓滑處理問題的方式或許就是你需要學習的。而作為公司的老板們也是最多和這類人員交涉的,因為他們了解公司的整體的宗旨方案,也了解客戶的需求,他們的老練思維往往會讓老板更加樂于傾聽。這也不難想象為什么這類人員有時候拿錢或許比你做孺子牛的自己來說更多的緣故了,更加受到器重讓你有嫉妒之嫌。你可曾想過如果老板讓你去做,自己又能否勝任呢?呵呵。

            說到這里可能大家會覺得我講的似乎在說一名老練的銷售業(yè)務人員嗎?沒錯,如果你想做一個高端的邊緣技術人員(說到這自己都感覺有點不大確切了,呵呵)上面談到的那種圓滑是你必修課程,但是有這些個人認為還遠遠不夠!為什么這么說呢?在我看來,過去一些年確實很多做銷售的人員比做純技術的人員賺錢多這個也是不爭的事實。在未來如果你想在這個職位闖蕩,需要的素養(yǎng)再也不是靠過去的牛皮嘴了更多的是需要一個務實的干練大氣者。你需要知道在什么場合說話到位的分寸;你需要知道如何行云流水般的寫一份好材料;你需要有細心的觀察和不失吝嗇的豪邁口才;更甚你還不能缺少酒桌文化。。。。。。所有的這些或許你不具備,每個人都不是天生賦予有這些能力,但是你只要還年輕懂得如何去就是你最大的資本。當然這些前提都是需要你性格已經(jīng)具備它的潛質(zhì)所在,不然勸你另擇道路或許也是你的一片天地呢?

            寫到這回過頭看看,仿佛這個并不是自己的所謂總結了,連我自己都覺得自己都在天馬行空,哈哈。因為我們聽過太多的領導的年終評述,八股文的段落著實讓自己不想再陳詞濫調(diào)一番在這辭舊迎新的新春,借著黨的春風我們回顧過去,展望未來在接下來的09年。。。

            既然09年馬上要到來,我也簡述下吧,俗是俗了點但是形式上還是要走一下的,呵呵。對于個人發(fā)展來說,09年自己還是想有個新的環(huán)境。人老在一個地方呆容易把自己給停滯成為慣性的懶惰。盡管自己有很多想法,但是有想法確實是不夠的,需要著實的執(zhí)行一番。在09年開始有空就寫blog了,做看官N年之久。把自己的一些心得體會寫出來。若干年之后自己回頭看看或許是一種美妙的回憶呢?呵呵。在09年繼續(xù)鞏固自己的技術基石,同時也開始學習一門新語言:Erlang。在它的邁進同時還是不忘JAVA給我?guī)頍o窮樂資。最后就是希望上天多給我一些機會,我時刻準備著,哈哈。相信自己一定行!

    (唧唧歪歪這么多寫到這算都結束了!不知所云,哈哈)

    PS:“有想法是不夠的,執(zhí)行力度還很差”---崔,我非常謝謝有你這個朋友,你的直率和指點讓我知道太多的無知,也讓我知道人的奮斗目標是什么;盡管有時候你喜歡給我吃棒棒糖,內(nèi)心甜滋滋的,但正是你的這些語言從某些側面刺激了我的思維,很是感謝你!真的。我希望我們09年我們都共勉。 

    08年總結評述(一)

    08年總結評述(二)

    08年總結評述(三)

    posted @ 2008-12-08 01:48 葉澍成 閱讀(1604) | 評論 (1)編輯 收藏
    主站蜘蛛池模板: 午夜在线a亚洲v天堂网2019| 国产免费区在线观看十分钟| 久久精品国产亚洲香蕉| 日韩免费视频播播| 美女内射毛片在线看免费人动物| jizz免费一区二区三区| 国产天堂亚洲精品| 国产精品高清视亚洲一区二区| 亚洲性猛交XXXX| 亚洲精品一级无码鲁丝片| 好爽又高潮了毛片免费下载| 1000部禁片黄的免费看| 免费一级不卡毛片| 精品一区二区三区高清免费观看 | 精品国产亚洲AV麻豆| 亚洲视频在线免费播放| 国产V亚洲V天堂A无码| 国产亚洲人成A在线V网站| 婷婷亚洲天堂影院| 国产午夜影视大全免费观看 | 亚洲AV永久无码精品网站在线观看| 亚洲国产亚洲片在线观看播放| 亚洲av无码成h人动漫无遮挡| 国产啪亚洲国产精品无码| 又黄又爽一线毛片免费观看 | 亚洲熟妇自偷自拍另欧美| 亚洲中文字幕人成乱码| 亚洲精品成人网站在线播放| 99人中文字幕亚洲区 | 最近2018中文字幕免费视频| 一级毛片在线免费看| 日韩免费高清大片在线| 99久久99热精品免费观看国产| 日本免费人成网ww555在线| 中文字幕乱码免费看电影| av永久免费网站在线观看| 一个人看的www免费视频在线观看 一个人免费视频观看在线www | 亚洲欧洲精品视频在线观看| 亚洲日本在线免费观看| 亚洲jjzzjjzz在线播放| 伊人久久亚洲综合影院首页|