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

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

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

    Java Votary

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      48 隨筆 :: 1 文章 :: 80 評論 :: 0 Trackbacks

    2006年1月4日 #

    ThreadLocal是什么

    早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優(yōu)美的多線程程序。

    ThreadLocal很容易讓人望文生義,想當(dāng)然地認(rèn)為是一個“本地線程”。其實,ThreadLocal并不是一個Thread,而是Thread的局部變量,也許把它命名為ThreadLocalVariable更容易讓人理解一些。

    當(dāng)使用ThreadLocal維護(hù)變量時,ThreadLocal為每個使用該變量的線程提供獨(dú)立的變量副本,所以每一個線程都可以獨(dú)立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本。

    從線程的角度看,目標(biāo)變量就象是線程的本地變量,這也是類名中“Local”所要表達(dá)的意思。

    線程局部變量并不是Java的新發(fā)明,很多語言(如IBM IBM XL FORTRAN)在語法層面就提供線程局部變量。在Java中沒有提供在語言級支持,而是變相地通過ThreadLocal的類提供支持。

    所以,在Java中編寫線程局部變量的代碼相對來說要笨拙一些,因此造成線程局部變量沒有在Java開發(fā)者中得到很好的普及。

    ThreadLocal的接口方法

    ThreadLocal類接口很簡單,只有4個方法,我們先來了解一下:

    • void set(Object value)

    設(shè)置當(dāng)前線程的線程局部變量的值。

    • public Object get()

    該方法返回當(dāng)前線程所對應(yīng)的線程局部變量。

    • public void remove()

    將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對應(yīng)該線程的局部變量將自動被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。

    • protected Object initialValue()

    返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設(shè)計的。這個方法是一個延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實現(xiàn)直接返回一個null。

    值得一提的是,在JDK5.0中,ThreadLocal已經(jīng)支持泛型,該類的類名已經(jīng)變?yōu)門hreadLocal<T>。API方法 也相應(yīng)進(jìn)行了調(diào)整,新版本的API方法分別是void set(T value)、T get()以及T initialValue()。

    ThreadLocal是如何做到為每一個線程維護(hù)變量的副本的呢?其實實現(xiàn)的思路很簡單:在ThreadLocal類中有一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應(yīng)線程的變量副本。我們自己就可以提供一個簡單的實現(xiàn)版本:

    代碼清單1 SimpleThreadLocal

    public class SimpleThreadLocal {

    private Map valueMap = Collections.synchronizedMap(new HashMap());

    public void set(Object newValue) {

    valueMap.put(Thread.currentThread(), newValue);①鍵為線程對象,值為本線程的變量副本

    }

    public Object get() {

    Thread currentThread = Thread.currentThread();

    Object o = valueMap.get(currentThread);②返回本線程對應(yīng)的變量

    if (o == null && !valueMap.containsKey(currentThread)) {③如果在Map中不存在,放到Map

    中保存起來。

    o = initialValue();

    valueMap.put(currentThread, o);

    }

    return o;

    }

    public void remove() {

    valueMap.remove(Thread.currentThread());

    }

    public Object initialValue() {

    return null;

    }

    }

    雖然代碼清單9?3這個ThreadLocal實現(xiàn)版本顯得比較幼稚,但它和JDK所提供的ThreadLocal類在實現(xiàn)思路上是相近的。

    一個TheadLocal實例

    下面,我們通過一個具體的實例了解一下ThreadLocal的具體使用方法。

    代碼清單2 SequenceNumber

    package com.baobaotao.basic;

    public class SequenceNumber {

    通過匿名內(nèi)部類覆蓋ThreadLocalinitialValue()方法,指定初始值

    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){

    public Integer initialValue(){

    return 0;

    }

    };

    獲取下一個序列值

    public int getNextNum(){

    seqNum.set(seqNum.get()+1);

    return seqNum.get();

    }

    public static void main(String[] args)

    {

    SequenceNumber sn = new SequenceNumber();

    3個線程共享sn,各自產(chǎn)生序列號

    TestClient t1 = new TestClient(sn);

    TestClient t2 = new TestClient(sn);

    TestClient t3 = new TestClient(sn);

    t1.start();

    t2.start();

    t3.start();

    }

    private static class TestClient extends Thread

    {

    private SequenceNumber sn;

    public TestClient(SequenceNumber sn) {

    this.sn = sn;

    }

    public void run()

    {

    for (int i = 0; i < 3; i++) {④每個線程打出3個序列值

    System.out.println("thread["+Thread.currentThread().getName()+

    "] sn["+sn.getNextNum()+"]");

    }

    }

    }

    }

     

    通常我們通過匿名內(nèi)部類的方式定義ThreadLocal的子類,提供初始的變量值,如例子中①處所示。TestClient線程產(chǎn)生一組序列號, 在③處,我們生成3個TestClient,它們共享同一個SequenceNumber實例。運(yùn)行以上代碼,在控制臺上輸出以下的結(jié)果:

    thread[Thread-2] sn[1]

    thread[Thread-0] sn[1]

    thread[Thread-1] sn[1]

    thread[Thread-2] sn[2]

    thread[Thread-0] sn[2]

    thread[Thread-1] sn[2]

    thread[Thread-2] sn[3]

    thread[Thread-0] sn[3]

    thread[Thread-1] sn[3]

    考察輸出的結(jié)果信息,我們發(fā)現(xiàn)每個線程所產(chǎn)生的序號雖然都共享同一個SequenceNumber實例,但它們并沒有發(fā)生相互干擾的情況,而是各自產(chǎn)生獨(dú)立的序列號,這是因為我們通過ThreadLocal為每一個線程提供了單獨(dú)的副本。

    Thread同步機(jī)制的比較

    ThreadLocal和線程同步機(jī)制相比有什么優(yōu)勢呢?ThreadLocal和線程同步機(jī)制都是為了解決多線程中相同變量的訪問沖突問題。

    在同步機(jī)制中,通過對象的鎖機(jī)制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機(jī)制要求程序慎密地分析什么時候?qū)ψ兞窟M(jìn)行讀寫,什么時候需要鎖定某個對象,什么時候釋放對象鎖等繁雜的問題,程序設(shè)計和編寫難度相對較大。

    而ThreadLocal則從另一個角度來解決多線程的并發(fā)訪問。ThreadLocal會為每一個線程提供一個獨(dú)立的變量副本,從而隔離了多個線 程對數(shù)據(jù)的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進(jìn)行同步了。ThreadLocal提供了線程安全的共享對象,在編 寫多線程代碼時,可以把不安全的變量封裝進(jìn)ThreadLocal。

    由于ThreadLocal中可以持有任何類型的對象,低版本JDK所提供的get()返回的是Object對象,需要強(qiáng)制類型轉(zhuǎn)換。但JDK 5.0通過泛型很好的解決了這個問題,在一定程度地簡化ThreadLocal的使用,代碼清單 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。

    概括起來說,對于多線程資源共享的問題,同步機(jī)制采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。

    Spring使用ThreadLocal解決線程安全問題

    我們知道在一般情況下,只有無狀態(tài)的Bean才可以在多線程環(huán)境下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用 域。就是因為Spring對一些Bean(如RequestContextHolder、 TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態(tài)采用 ThreadLocal進(jìn)行處理,讓它們也成為線程安全的狀態(tài),因為有狀態(tài)的Bean就可以在多線程中共享了。

    一般的Web應(yīng)用劃分為展現(xiàn)層、服務(wù)層和持久層三個層次,在不同的層中編寫對應(yīng)的邏輯,下層通過接口向上層開放功能調(diào)用。在一般情況下,從接收請求到返回響應(yīng)所經(jīng)過的所有程序調(diào)用都同屬于一個線程,如圖9?2所示:

    通通透透理解ThreadLocal

    1同一線程貫通三層

    這樣你就可以根據(jù)需要,將一些非線程安全的變量以ThreadLocal存放,在同一次請求響應(yīng)的調(diào)用線程中,所有關(guān)聯(lián)的對象引用到的都是同一個變量。

    下面的實例能夠體現(xiàn)Spring對有狀態(tài)Bean的改造思路:

    代碼清單3 TopicDao:非線程安全

    public class TopicDao {

    private Connection conn;一個非線程安全的變量

    public void addTopic(){

    Statement stat = conn.createStatement();引用非線程安全變量

    }

    }

    由于①處的conn是成員變量,因為addTopic()方法是非線程安全的,必須在使用時創(chuàng)建一個新TopicDao實例(非singleton)。下面使用ThreadLocal對conn這個非線程安全的“狀態(tài)”進(jìn)行改造:

    代碼清單4 TopicDao:線程安全

    import java.sql.Connection;

    import java.sql.Statement;

    public class TopicDao {

    ①使用ThreadLocal保存Connection變量

    private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();

    public static Connection getConnection(){

    ②如果connThreadLocal沒有本線程對應(yīng)的Connection創(chuàng)建一個新的Connection,

    并將其保存到線程本地變量中。

    if (connThreadLocal.get() == null) {

    Connection conn = ConnectionManager.getConnection();

    connThreadLocal.set(conn);

    return conn;

    }else{

    return connThreadLocal.get();③直接返回線程本地變量

    }

    }

    public void addTopic() {

    ④從ThreadLocal中獲取線程對應(yīng)的Connection

    Statement stat = getConnection().createStatement();

    }

    }

    不同的線程在使用TopicDao時,先判斷connThreadLocal.get()是否是null,如果是null,則說明當(dāng)前線程還沒有對 應(yīng)的Connection對象,這時創(chuàng)建一個Connection對象并添加到本地線程變量中;如果不為null,則說明當(dāng)前的線程已經(jīng)擁有了 Connection對象,直接使用就可以了。這樣,就保證了不同的線程使用線程相關(guān)的Connection,而不會使用其它線程的 Connection。因此,這個TopicDao就可以做到singleton共享了。

    當(dāng)然,這個例子本身很粗糙,將Connection的ThreadLocal直接放在DAO只能做到本DAO的多個方法共享Connection時 不發(fā)生線程安全問題,但無法和其它DAO共用同一個Connection,要做到同一事務(wù)多DAO共享同一Connection,必須在一個共同的外部類 使用ThreadLocal保存Connection。

    小結(jié)

    ThreadLocal是解決線程安全問題一個很好的思路,它通過為每個線程提供一個獨(dú)立的變量副本解決了變量并發(fā)訪問的沖突問題。在很多情況 下,ThreadLocal比直接使用synchronized同步機(jī)制解決線程安全問題更簡單,更方便,且結(jié)果程序擁有更高的并發(fā)性。

    posted @ 2009-07-01 12:15 Dion 閱讀(346) | 評論 (0)編輯 收藏

         摘要: 題記世界是事實的總體,而不是事物的總體。世界為諸事實所規(guī)定,為它們既是全部事實所規(guī)定。——路德維希·維特根斯坦,《邏輯哲學(xué)論》The answer, please?"請回答我..."The stern voice startles you. 一個嚴(yán)厲的聲音把你嚇個半死.You were dozing in Mrs. Rosencrantz's high school math class agai...  閱讀全文
    posted @ 2006-03-11 10:02 Dion 閱讀(2112) | 評論 (0)編輯 收藏

    前幾天跟著寫了一個簡單的例子.
    覺得Drools的配置也沒有什么.
    今天在運(yùn)行house的例子的時候, 無論怎么樣, 總是異常: 沒有定義的SMF.
    顯然沒有找到我定義的drools.config文件.
    官方網(wǎng)站上是這樣寫地:
    String droolsConfigProp = System.getProperty( "drools.conf" );

    if ( droolsConfigProp != null )
    {
        loadConfig( droolsConfigProp );
    }

    ClassLoader cl = Thread.currentThread( ).getContextClassLoader( ); if ( cl == null )
    {
        cl = getClass( ).getClassLoader( );
    }

    Enumeration configUrls = cl.getResources( "META-INF/drools.conf" );

    if ( !configUrls.hasMoreElements( ) )
    {
        cl = getClass( ).getClassLoader( );
        configUrls = cl.getResources( "META-INF/drools.conf" );
    }

    if ( !configUrls.hasMoreElements( ) )
    {
        cl = ClassLoader.getSystemClassLoader( );
        configUrls = cl.getResources( "META-INF/drools.conf" );
    }

    this.classLoader = cl;
    while ( configUrls.hasMoreElements( ) )
    {
        URL configUrl = (URL) configUrls.nextElement( );
        loadConfig( configUrl );
    }

    好像每一個旮旯里面都找了, 為什么沒有找到我的呢?
    System.getProperty指向的位置并不一定和loadFromUrl位置一樣.呵呵.
    posted @ 2006-03-11 10:00 Dion 閱讀(1640) | 評論 (0)編輯 收藏

    內(nèi)容提要
           在本文的第一部分,我將討論規(guī)則引擎如何幫助你從軟件的應(yīng)用邏輯中分離出商業(yè)規(guī)則邏輯,以實現(xiàn)商業(yè)應(yīng)用的靈活性。另外,我還將介紹JSR-94規(guī)則引擎 API,及其開源實現(xiàn)Drools項目,它是這一新技術(shù)的先驅(qū)。在第二部分,我們將介紹一個規(guī)則引擎例子,并深入地研究Drools引擎及其JSR-94 擴(kuò)展的復(fù)雜性。

    為什么使用規(guī)則引擎
           商業(yè)世界充滿了關(guān)于變化的陳詞濫調(diào),如任何事物都會改變,唯一不變的是變化等等。而在技術(shù)領(lǐng)域里,情況正好相反。我們?nèi)匀辉谠噲D解決30年前軟件業(yè)中同樣 的一堆問題--也許比30年前還要多的問題。在過去的十年,IT從業(yè)人員淹沒在軟件方法學(xué)的大量文獻(xiàn)中,如快速軟件開發(fā),極限編程,敏捷軟件開發(fā)等,它們 無一例外地強(qiáng)調(diào)靈活和變化的重要性。
           但商業(yè)通常比開發(fā)團(tuán)隊所依賴的軟件過程和技術(shù)改變得更加迅速。當(dāng)商業(yè)策劃人員試圖重整IT部門,以支持新的業(yè)務(wù)轉(zhuǎn)型時,仍然覺得很費(fèi)勁。

    Lost in Translation
           雖然IT團(tuán)隊反應(yīng)迅速,但他們通常帶來"電話效應(yīng)"――IT給商業(yè)計劃的執(zhí)行帶來的阻力和它帶來的利益一樣多。不幸的是,在開發(fā)團(tuán)隊完全理解商業(yè)決策規(guī)則 并實現(xiàn)之前,規(guī)則已經(jīng)改變了。在軟件進(jìn)入市場前,它已經(jīng)過時了,需要進(jìn)行重構(gòu)以滿足新的業(yè)務(wù)需求。如果你是一個開發(fā)人員,你會知道我在說什么。再也沒有比 在需求變動的情況下構(gòu)造軟件讓開發(fā)人員更沮喪的事情了。作為軟件開發(fā)人員,你必須比業(yè)務(wù)人員更了解業(yè)務(wù),有時還要了解更多。
           試想一下你是一位商業(yè)決策者。假如公司的成功依賴于你對于市場趨勢敏銳的洞察力,它常常幫助你領(lǐng)先于競爭者利用變化的市場環(huán)境獲利。每天你都會得到更多更 好的市場信息,但并不要緊。完成新產(chǎn)品開發(fā)可能需要6-9個月,在此期間,對于市場大膽和敏銳的洞察和信息優(yōu)勢可能已經(jīng)浪費(fèi)了。而且,當(dāng)產(chǎn)品發(fā)布時,有這 樣幾種可能:產(chǎn)品沒有什么吸引人的特性,預(yù)算超支,過了產(chǎn)品的最佳發(fā)布期限,或三者兼而有之。
           情況可能還會更糟,在完成產(chǎn)品開發(fā)時,市場環(huán)境和規(guī)劃產(chǎn)品開發(fā)時相比,已經(jīng)發(fā)生了根本變化。現(xiàn)在你必須要遵守新的規(guī)則,你已經(jīng)喪失了你的邊際優(yōu)勢,而且設(shè) 計軟件的五人中的三人已經(jīng)離開了公司。你必須給接手的新人重新講解復(fù)雜的業(yè)務(wù)。如果事情不順利,你可能發(fā)現(xiàn)自己要對付一個缺少文檔,并且你完全不了解的遺 留應(yīng)用。
           你的戰(zhàn)略在哪出現(xiàn)了問題?你在哪里應(yīng)該可以做到更好?最近的輕量級軟件過程,如極限編程,敏捷軟件開發(fā)等都在強(qiáng)調(diào)自動單元測試和軟件功能優(yōu)先級的重要性。 除此之外,還有其他的原則,你的開發(fā)團(tuán)隊可能也很熟悉,這些原則可以幫助他們對需求的變動作出迅速反應(yīng)并縮短項目的開發(fā)周期。這些原則的大多數(shù),如系統(tǒng)分 解,多年前就已經(jīng)出現(xiàn),并得到了Java平臺的支持(如JMX等),還有如面向?qū)ο蠛徒巧#呀?jīng)內(nèi)建在Java語言中。
           但Java仍然是一門相當(dāng)年輕的語言,而且Java平臺遠(yuǎn)遠(yuǎn)還沒有完備。當(dāng)前在Java社區(qū),一個引人注目的新技術(shù)是,分離商業(yè)決策者的商業(yè)決策邏輯和應(yīng) 用開發(fā)者的技術(shù)決策,并把這些商業(yè)決策放在中心數(shù)據(jù)庫,讓它們能在運(yùn)行時(即商務(wù)時間)可以動態(tài)地管理和修改。這是一個你值得考慮的策略。
           為什么你的開發(fā)團(tuán)隊不得不象商業(yè)經(jīng)理人一樣,在代碼中包含復(fù)雜微妙的商業(yè)決策邏輯呢?你怎樣才能向他們解釋決策推理的微妙之處呢?你這樣做是否謹(jǐn)慎呢?可 能不是。象bottom line一樣,某些東西在解釋的過程中丟失了。為什么要冒這樣的風(fēng)險,讓應(yīng)用代碼或測試代碼錯誤地表達(dá)你的商業(yè)決策邏輯呢?如果這樣做的話,你怎樣檢查它 們的正確性呢――難道你自己想學(xué)習(xí)如何編程和編寫測試代碼,或者你的客戶會為你測試軟件?你一方面要應(yīng)付市場,一方面要應(yīng)付軟件代碼,這實在太困難了。
           如果能將這些商業(yè)決策規(guī)則集中地放在一個地方,以一種你可以理解的格式定義,讓你可以直接管理,而不是散落在代碼的各個角落,那該有多好。如果你能把商業(yè) 決策規(guī)則獨(dú)立于你的軟件代碼,讓開發(fā)團(tuán)隊作出技術(shù)決策,你將會獲得更多好處。你的項目開發(fā)周期會更短,軟件對于變動的需求更靈活。

    規(guī)則引擎標(biāo)準(zhǔn)Java API
           2003年11月,Java社區(qū)通過了Java Rule Engine API規(guī)范(JSR-94)的最后草案。這個新的API讓開發(fā)人員在運(yùn)行時訪問和執(zhí)行規(guī)則有了統(tǒng)一的標(biāo)準(zhǔn)方式。隨著新規(guī)范產(chǎn)品實現(xiàn)的成熟和推向市場,開發(fā) 團(tuán)隊將可以從應(yīng)用代碼中抽取出商業(yè)決策邏輯。
           這就需要新一代的管理工具,幫助商務(wù)經(jīng)理人可以定義和細(xì)化軟件系統(tǒng)的行為。不必通過開發(fā)過程來修改應(yīng)用,并假定可以得到正確的結(jié)果,經(jīng)理人將可以隨時根據(jù)需要修改決策規(guī)則,并進(jìn)行測試。
           但這將需要開發(fā)人員在設(shè)計系統(tǒng)時作出某些改變,并可以得到合適的開發(fā)工具。

    分離商務(wù)和技術(shù)的關(guān)注點
           這是一個非常簡單的例子,從經(jīng)理人的角度,說明如何分離商務(wù)和技術(shù)的關(guān)注點。
           你管理著一個反向投資基金。你公司計算機(jī)系統(tǒng)的一部分用于分析股票價格,收益和每股凈資產(chǎn),并在需要時向你提出預(yù)警。這個計算機(jī)系統(tǒng)的工作是,識別出PE比率比市場平均值低的股票,并標(biāo)記出來以便進(jìn)一步的檢查。
           你的IT部門擁有一大堆數(shù)據(jù),并開發(fā)了一系列你可以在規(guī)則中引用的簡單數(shù)據(jù)對象。現(xiàn)在,為簡單起見,假設(shè)你是一名受過良好教育的,了解技術(shù)的管理人,你了解XML的基本知識,可以讓你編寫和修改簡單的XML規(guī)則文件。
           你的第一個規(guī)則是,給道瓊斯所有的股票估值,并剔除P/E比率大于10的股票(這有點過分簡化,但這里只作為一個例子)。保留下來的股票用來生產(chǎn)一系列報表。對于這個簡單的例子,你的規(guī)則文件看起來如下(我們將會過頭來討論這個文件的結(jié)構(gòu)):

    <stock:overvalued>
        <stock:index> DJIA </stock:index>
        <stock:pe> over 10.0 </stock:pe>
    </stock:overvalued>

           一個月后,你接到一家巴西分析師公司的電話,雇傭你的公司生成一系列巴西股市的報表,但他們有更嚴(yán)格的標(biāo)準(zhǔn)。而目前在巴西,P/E比率市場平均值是個位 數(shù),因此你用來評估被市場低股票的閾值需要改變。除了較低的P/E比率,你的新客戶還要求以Price-to-Book比率作為參考標(biāo)準(zhǔn)。
           你啟動規(guī)則編輯器,并修改規(guī)則以匹配新的評估條件。現(xiàn)在,規(guī)則引擎剔除巴西股市中P/E比率大于6.5,以及Price to Book 比率小于等于1的股票。完成規(guī)則文件修改后,看起來如下:

    <stock:overvalued>
        <stock:index> Brazil </stock:index>
        <stock:pe> over 6.5 </stock:pe>
        <stock:pb> over 1.0 </stock:pb>
    </stock:overvalued>

           你無需為此向開發(fā)團(tuán)隊作任何解釋。你無需等待他們開發(fā)或測試程序。如果你的規(guī)則引擎的語義足夠強(qiáng)大,讓你描述工作數(shù)據(jù),你可以隨時按需修改商業(yè)規(guī)則。
           如果限制因素是規(guī)則的定義語言和數(shù)據(jù)模型,你可以確信這兩者將會標(biāo)準(zhǔn)化,并出現(xiàn)先進(jìn)的編輯器和工具,以簡化規(guī)則的定義,保存和維護(hù)。
           現(xiàn)在,我希望你已經(jīng)清楚以下的原則:在這個例子中,哪只股票是否被選擇是一個商務(wù)決策,而不是技術(shù)決策。決定將哪只股票交給你的分析師是經(jīng)理人的邏輯 ――"logic of the bottom line"。經(jīng)理人作出這些決策,并可以按需定制應(yīng)用。這些規(guī)則因此變成了一種控制界面,一種新的商業(yè)系統(tǒng)用戶界面。

    使用Rule開發(fā)
           如果在這個應(yīng)用場景中,你是一個開發(fā)人員,你的工作會稍微輕松一些。一旦你擁有了一種用于分析股票的規(guī)則語言,你可以取出數(shù)據(jù)對象并交給規(guī)則引擎執(zhí)行。我們將會到規(guī)則語言的討論,但現(xiàn)在我們繼續(xù)剛才的例子。
           你的系統(tǒng)將一系列的stock bean輸入規(guī)則引擎。當(dāng)規(guī)則執(zhí)行后,你可以選出符合條件的股票并可以對它們作進(jìn)一步處理。也許是把它們輸入報表生成系統(tǒng)。分析師使用這些報表幫助他們分 析股市。同時,老板也可能讓你使用新的技術(shù)分析工具,并用Dow理論預(yù)測股市的底部和頂部。
           規(guī)則引擎可以讓你的系統(tǒng)變得更簡單,因為你無需在代碼中編寫商務(wù)邏輯,如怎樣選擇股票,選擇股票過程中奇怪的條件組合等。這些邏輯不再進(jìn)入你的代碼。你將可以專注于數(shù)據(jù)模型。
           現(xiàn)在可以這么認(rèn)為,通過從應(yīng)用代碼中剝離出易變的商業(yè)邏輯,你的效率會更高。但凡是總有例外――簡單應(yīng)用可能并不能從規(guī)則系統(tǒng)中獲益。但如果你開發(fā)一個大型系統(tǒng),有很多易變的商業(yè)邏輯,你可以考慮在應(yīng)用中集成規(guī)則引擎。
           除了從應(yīng)用代碼中剝離出商業(yè)決策邏輯外,規(guī)則引擎還有其他用處。有時候你需要應(yīng)用成百上千的規(guī)則進(jìn)行決策,并且有上千個對象和這些規(guī)則一起使用。很難想象 有什么先進(jìn)的人工智能引擎可以處理這種情況。遇到這種情況,你需要一個極快的決策算法或是大型機(jī)。大型機(jī)并不便宜,但你可以非常便宜的得到效率和可伸縮性 最好的算法。

    Bob McWhirter的Drools項目
           現(xiàn)在,我要介紹Drools項目,Charles Forgy Rete算法的一個增強(qiáng)的Java語言實現(xiàn)。Drools是一個Bob McWhirter開發(fā)的開源項目,放在The Codehaus上。在我寫這篇文章時,Drools發(fā)表了2.0-beata-14版。在CVS中,已完整地實現(xiàn)了JSR94 Rule Engine API并提供了單元測試代碼。
           Rete算法是Charles Forgy在1979年發(fā)明的,是目前用于生產(chǎn)系統(tǒng)的效率最高的算法(除了私有的Rete II)。Rete是唯一的,效率與執(zhí)行規(guī)則數(shù)目無關(guān)的決策支持算法。For the uninitiated, that means it can scale to incorporate and execute hundreds of thousands of rules in a manner which is an order of magnitude more efficient then the next best algorithm。Rete應(yīng)用于生產(chǎn)系統(tǒng)已經(jīng)有很多年了,但在Java開源軟件中并沒有得到廣泛應(yīng)用(討論Rete算法的文檔參見http://herzberg.ca.sandia.gov/jess/docs/61/rete.html。)。
           除了應(yīng)用了Rete核心算法,開源軟件License和100%的Java實現(xiàn)之外,Drools還提供了很多有用的特性。其中包括實現(xiàn)了JSR94 API和創(chuàng)新的規(guī)則語義系統(tǒng),這個語義系統(tǒng)可用來編寫描述規(guī)則的語言。目前,Drools提供了三種語義模塊――Python模塊,Java模塊和 Groovy模塊。本文余下部分集中討論JSR94 API,我將在第二篇文章中討論語義系統(tǒng)。
           作為使用javax.rules API的開發(fā)人員,你的目標(biāo)是構(gòu)造一個RuleExecutionSet對象,并在運(yùn)行時通過它獲得一個RuleSession對象。為了簡化這個過程, 我編寫了一個規(guī)則引擎API的fa?ade,可以用來解釋代表Drools的DRL文件的InputStream,并構(gòu)造一個 RuleExecutionSet對象。
           在上面提到了Drools的三種語義模塊,我接下來使用它們重新編寫上面的例子XML規(guī)則文件。這個例子中我選擇Java模塊。使用Java模塊重新編寫的規(guī)則文件如下:

    <rule-set name="StockFlagger"
          xmlns="http://drools.org/rules"
          xmlns:java="http://drools.org/semantics/java">
      <rule name="FlagAsUndervalued">
        <parameter identifier="stock">
          <java:class>org.codehaus.drools.example.Stock</java:class>
        </parameter>
        <java:condition>stock.getIndexName().equals("DJIA");</java:condition>
        <java:condition>stock.getPE() > 10 </java:condition>
        <java:consequence>
          removeObject(stock);   ( 譯注:應(yīng)該是retractObject(stock) )
        </java:consequence>
      </rule>
    </rule-set>

           現(xiàn)在的規(guī)則文件并沒有上面的簡潔明了。別擔(dān)心,我們將在下一篇文章討論語義模塊。現(xiàn)在,請注意觀察XML文件的結(jié)構(gòu)。其中一個rule-set元素包含了 一個或多個rule元素,rule元素又包含了parameter,condition和consequence元素。Condition和 consequence元素包含的內(nèi)容和Java很象。注意,在這些元素中,有些事你可以做,有些事你不能做。目前,Drools使用 BeanShell2.0b1作為它的Java解釋器。我在這里并不想詳細(xì)的討論DRL文件和Java語義模塊的語法。我們的目標(biāo)是解釋如何使用 Drools的JSR94 API。
           在Drools項目CVS的drools-jsr94模塊中,單元測試代碼包含了一個ExampleRuleEngineFacade對象,它基于 Brian Topping的Dentaku項目。這個fa?ade對象通過javax.rules API,創(chuàng)建了供RuleExecutionSet和RuleSession使用的一系列對象。它并沒有完全包括了Drools引擎API的所有特性和細(xì) 微差別,但可以作為新手使用API的一個簡單例子。
          下面的代碼片斷顯示如何使用規(guī)則引擎的facade構(gòu)造一個RuleExecutionSet對象,并通過它獲得一個RuleSession對象。
     
    import java.io.InputStream;
    import javax.rules.*;
    import org.drools.jsr94.rules.ExampleRuleEngineFacade;
    public class Example {
        private ExampleRuleEngineFacade engine;
        private StatelessRuleSession statelessSession;
        /* place the rule file in the same package as this class */
        private String bindUri = "myRuleFile.drl"
        public Example() {
            /* get your engine facade */
            engine = new ExampleRuleEngineFacade();
            /* get your input stream */
            InputStream inputStream =
                    Example.class.getResourceAsStream(bindUri);
            /* build a RuleExecutionSet to the engine */
            engine.addRuleExecutionSet(bindUri, inputStream);
            /* don't forget to close your InputStream! */
            inputStream.close();
            /* get your runtime session */
            this.statelessSession = engine.getStatelessRuleSession(bindUri);
        }
        ...
    }

           在以上的例子代碼中,你需要處理InputStream的IOException例外,這里為了簡單起見省略了。你要做的只是構(gòu)建InputStream 對象,并把它輸入ExampleRuleEngineFacade,用來創(chuàng)建一個RuleExecutionSet對象。然后,你可以得到一個 StatelessRuleSession,并用它來執(zhí)行所有的規(guī)則。使用StatelessRuleSession相對簡單。我們可以給上面的類添加一 個方法,用來對一個對象列表執(zhí)行規(guī)則:

    public List getUndervalued(List stocks) {
        return statelessSession.executeRules(stocks);
    }

           該方法輸入一個stock對象列表給規(guī)則引擎,然后使用規(guī)則評估輸入的股票對象,并剔除那些不符合價值低估標(biāo)準(zhǔn)的股票。它是個簡單的例子,但足以說明問題。
           在ExampleRuleEngineFacade類中,代碼會稍微有些復(fù)雜。ExampleRuleEngineFacade類創(chuàng)建了一個 RuleServiceProvider對象,并用它創(chuàng)建RuleAdministrator,RuleExecutionSetProvider和 RuleRuntime對象。RuleExecutionSetProvider負(fù)責(zé)解釋InputStream,并創(chuàng)建一個 RuleExecutionSet對象。RuleRuntime對象用來得到一個session,RuleAdministrator用來管理所有的對 象。在往下是Drools核心API,它的核心是Rete算法實現(xiàn)。我在這里不打算詳細(xì)討論,但你可以看看 ExampleRuleEngineFacade的代碼。
           現(xiàn)在你已經(jīng)看到了在商業(yè)和科研方面使用規(guī)則引擎的一些例子,并對Drools項目有了基本的了解。在下一篇文章里,我將討論DRL文件的結(jié)構(gòu)和Java語 義模塊,讓你可以編寫自己的DRL文件。還將向你解釋如何編寫你自己的語義模塊,討論salience和working memory的概念。

    資源
    · Drools Project
    · JSR-94 Specification
     
    作者
          N. Alex Rupp is a freelance software architect and developer from Minneapolis, and the current JSR94 Lead for the Drools project.
    posted @ 2006-03-11 10:00 Dion 閱讀(1750) | 評論 (0)編輯 收藏

    一般情況下, 只顯式引用:

    • drools-all-2.0.jar
    • antlr-2.7.5.jar
    • xercesImpl-2.6.2.jar

    就可以了.當(dāng)然ClassPath下也要用一些其他的jar.
    下載位置: http://dist.codehaus.org/drools/distributions/drools-2.0-bin-withdeps.zip

    如果, 在DRL文件中定義了Java Function, 這時候就要顯式的引用:

    • janino-2.3.2.jar

    這時候, 引擎是需要janino把DRL中的java function描述轉(zhuǎn)換成可執(zhí)行的二進(jìn)制代碼(?)的.

    posted @ 2006-03-11 09:59 Dion 閱讀(962) | 評論 (0)編輯 收藏

    Drools and Mandarax

    兩個項目做了兩件不同的事情: 一個是Forward Chaining,另一個是 backward chaining. Drools 是forward chaining的,  意味著 它對assert的對象反應(yīng), 事件驅(qū)動的. Mandarax 是 backward chaining的, 像 prologue一樣, 你問它問題, 它試圖給你它知道的答案. 舉例來說, 在使用Drools的時候, 你可能會先assert 給它今天的日期, 如果它發(fā)現(xiàn)有匹配的規(guī)則的手,它會用事件的方式通知你"今天是你的生日". 在 backward chaining 的系統(tǒng), 你可能先問 "今天是我的生日嘛?" 系統(tǒng)會搜索它知道的, 然后告訴你答案.
    For an excellent explanation of forward and backward chaining read Charles Forgey's recent articles at http://rulespower.com/ - Forward and Backward Chaining:
    Parts 1, 2 and 3.
    posted @ 2006-03-11 09:58 Dion 閱讀(1175) | 評論 (0)編輯 收藏

    Open Source Rule Engines Written In Java

    • Drools The drools engine uses a modified form of the Rete algorithm called the Rete-OO algorithm. Internally it operates using the same concepts and methods as Forgy's original but adds some node types required for seemless integration with an object-oriented language.
    • OFBiz Rule Engine Backward chaining is supported. Original code base from "Building Parsers in Java" by Steven John Metsker.
    • Mandarax Based on backward reasoning. The easy integration of all kinds of data sources. E.g., database records can be easily integrated as sets of facts and reflection is used in order to integrate functionality available in the object model.
    • Algernon Efficient and concise KB traversal and retrieval. Straightforward access to ontology classes and instances. Supports both forward and backward chaining.
    • TyRuBa TyRuBa supports higher order logic programming: variables and compound terms are allowed everywhere in queries and rules, also in the position of a functor- or predicate-name. TyRuBa speeds up execution by making specialized copies of the rule-base for each query in the program. It does so incrementally while executing a logic program and builds an index for fast access to rules and facts in the rule base, tuned to the program that is running. The indexing techniques works also for higher-order logic. TyRuBa does 'tabling' of query results.
    • JTP Java Theorem Prover is based on a very simple and general reasoning architecture. The modular character of the architecture makes it easy to extend the system by adding new reasoning modules (reasoners), or by customizing or rearranging existing ones.
    • JEOPS JEOPS adds forward chaining, first-order production rules to Java through a set of classes designed to provide this language with some kind of declarative programming.
    • InfoSapient Semantics of business rules expressed using fuzzy logic.
    • JShop Simple Hierarchical Ordered Planner (SHOP) written in Java.
    • RDFExpert RDF-driven expert system shell. The RDFExpert software uses Brian McBride's JENA API and parser. A simple expert system shell that uses RDF for all of its input: knowledge base, inference rules and elements of the resolution strategy employed. It supports forward and backward chaining.
    • Jena 2 - Jena is a Java framework for writing Semantic Web applications. Jena2 has a reasoner subsystem which includes a generic rule based inference engine together with configured rule sets for RDFS and for the OWL/Lite subset of OWL Full. These reasoners can be used to construct inference models which show the RDF statements entailed by the data being reasoned over. The subsystem is designed to be extensible so that it should be possible to plug a range of external reasoners into Jena, though worked examples of doing so are left to a future release.
    • JLisa - JLisa is a powerful framework for building business rules accessible to Java and it is compatible with JSR-94. JLisa is more powerful than Clips because it has the expanded benefit of having all the features from common lisp available. These features are essential for multi-paradigm software development
    • Euler - Euler is a backward-chaining reasoner enhanced with Euler path detection and will tell you whether a given set of facts and rules supports a given conclusion. Things are described in N3.
    • JLog - JLog is an implementation of a Prolog interpreter, written in Java. It includes built-in source editor, query panels, online help, animation primitives, and a GUI debugger.
    • Pellet OWL Reasoner - Pellet is an open-source Java based OWL DL reasoner. It can be used in conjunction with either Jena or OWL API libraries. Pellet API provides functionalities to see the species validation, check consistency of ontologies, classify the taxonomy, check entailments and answer a subset of RDQL queries (known as ABox queries in DL terminology). Pellet is an OWL DL reasoner based on the tableaux algorithms developed for expressive Description Logics.
    • Prova - Prova is derived from Mandarax Java-based inference system developed by Jens Dietrich. Prova extends Mandarax by providing a proper language syntax, native syntax integration with Java, and agent messaging and reaction rules. The development of this language was supported by the grant provided within the EU project GeneStream. In the project, the language is used as a rules-based backbone for distributed web applications in biomedical data integration.
    posted @ 2006-03-11 09:58 Dion 閱讀(1344) | 評論 (0)編輯 收藏

    始終會用上的Common BeanUtils

    Beanutils用了魔術(shù)般的反射技術(shù),實現(xiàn)了很多夸張有用的功能,都是C/C++時代不敢想的。無論誰的項目,始終一天都會用得上它。我算是后知后覺了,第一回看到它的時候居然錯過。

    1.屬性的動態(tài)getter、setter

    在這框架滿天飛的年代,不能事事都保證執(zhí)行g(shù)etter,setter函數(shù)了,有時候?qū)傩允且鶕?jù)名字動態(tài)取得的,就像這樣:  
    BeanUtils.getProperty(myBean,"code");
    而Common BeanUtils的更強(qiáng)功能在于可以直接訪問內(nèi)嵌對象的屬性,只要使用點號分隔。
    BeanUtils.getProperty(orderBean, "address.city");
    相比之下其他類庫的BeanUtils通常都很簡單,不能訪問內(nèi)嵌的對象,所以有時要用Commons BeanUtils來替換它們。

    BeanUtils還支持List和Map類型的屬性,如下面的語法即可取得Order的顧客列表中第一個顧客的名字
    BeanUtils.getProperty(orderBean, "customers[1].name");
    其中BeanUtils會使用ConvertUtils類把字符串轉(zhuǎn)為Bean屬性的真正類型,方便從HttpServletRequest等對象中提取bean,或者把bean輸出到頁面。
    而PropertyUtils就會原色的保留Bean原來的類型。

    2.BeanCompartor 動態(tài)排序

    還是通過反射,動態(tài)設(shè)定Bean按照哪個屬性來排序,而不再需要在實現(xiàn)bean的Compare接口進(jìn)行復(fù)雜的條件判斷。
    List peoples = ...; // Person對象的列表
    Collections.sort(peoples, new BeanComparator("age"));

    如果要支持多個屬性的復(fù)合排序,如"Order By lastName,firstName"

    ArrayList sortFields = new ArrayList();
    sortFields.add(new BeanComparator("lastName"));
    sortFields.add(new BeanComparator("firstName"));
    ComparatorChain multiSort = new ComparatorChain(sortFields);
    Collections.sort(rows,multiSort);

    其中ComparatorChain屬于jakata commons-collections包。
    如果age屬性不是普通類型,構(gòu)造函數(shù)需要再傳入一個comparator對象為age變量排序。
    另外, BeanCompartor本身的ComparebleComparator, 遇到屬性為null就會拋出異常, 也不能設(shè)定升序還是降序。這個時候又要借助commons-collections包的ComparatorUtils.

       Comparator mycmp = ComparableComparator.getInstance();
       mycmp = ComparatorUtils.nullLowComparator(mycmp);  //允許null
       mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序
       Comparator cmp = new BeanComparator(sortColumn, mycmp);
    3.Converter 把Request或ResultSet中的字符串綁定到對象的屬性

       經(jīng)常要從request,resultSet等對象取出值來賦入bean中,如果不用MVC框架的綁定功能的話,下面的代碼誰都寫膩了。

       String a = request.getParameter("a");
    bean.setA(a);
    String b = ....
    bean.setB(b);
    ......

    不妨寫一個Binder自動綁定所有屬性:

        MyBean bean = ...;
    HashMap map = new HashMap();
    Enumeration names = request.getParameterNames();
    while (names.hasMoreElements())
    {
    String name = (String) names.nextElement();
    map.put(name, request.getParameterValues(name));
    }
    BeanUtils.populate(bean, map);

        其中BeanUtils的populate方法或者getProperty,setProperty方法其實都會調(diào)用convert進(jìn)行轉(zhuǎn)換。
         但Converter只支持一些基本的類型,甚至連java.util.Date類型也不支持。而且它比較笨的一個地方是當(dāng)遇到不認(rèn)識的類型時,居然會拋 出異常來。 對于Date類型,我參考它的sqldate類型實現(xiàn)了一個Converter,而且添加了一個設(shè)置日期格式的函數(shù)。
    要把這個Converter注冊,需要如下語句:

        ConvertUtilsBean convertUtils = new ConvertUtilsBean();
       DateConverter dateConverter = new DateConverter();
       convertUtils.register(dateConverter,Date.class);



    //因為要注冊converter,所以不能再使用BeanUtils的靜態(tài)方法了,必須創(chuàng)建BeanUtilsBean實例
    BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());
    beanUtils.setProperty(bean, name, value);
    4 其他功能
    4.1 ConstructorUtils,動態(tài)創(chuàng)建對象
         public static Object invokeConstructor(Class klass, Object arg)
    4.2 MethodUtils,動態(tài)調(diào)用方法
        MethodUtils.invokeMethod(bean, methodName, parameter);

    4.3 PropertyUtils,當(dāng)屬性為Collection,Map時的動態(tài)讀取:
    Collection: 提供index
       BeanUtils.getIndexedProperty(orderBean,"items",1);
    或者
      BeanUtils.getIndexedProperty(orderBean,"items[1]");
    Map: 提供Key Value
      BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111 
    或者
      BeanUtils.getMappedProperty(orderBean, "items(111)") 

    4.4 PropertyUtils,直接獲取屬性的Class類型
         public static Class getPropertyType(Object bean, String name)
    4.5 動態(tài)Bean 用DynaBean減除不必要的VO和FormBean 
    posted @ 2006-01-15 20:20 Dion 閱讀(5094) | 評論 (2)編輯 收藏

         摘要: Migrate apps from Internet Explorer to MozillaHow to make Internet Explorer-specific Web applications work in Mozilla-based browsersDocument options Print this page'); //--> Print this page E-ma...  閱讀全文
    posted @ 2006-01-04 19:18 Dion 閱讀(1492) | 評論 (1)編輯 收藏

    主站蜘蛛池模板: 亚洲色偷偷狠狠综合网| 久久久久久亚洲Av无码精品专口| a免费毛片在线播放| 久久精品国产亚洲综合色| 国产高清不卡免费在线| 成人精品国产亚洲欧洲| 国产亚洲无线码一区二区| 黄色片在线免费观看| 免费看黄福利app导航看一下黄色录像| 伊人久久综在合线亚洲91| 精品香蕉在线观看免费| 成人午夜影视全部免费看| 亚洲嫩草影院在线观看| 亚洲?v无码国产在丝袜线观看| 久久久久国色av免费看| 欧美亚洲国产SUV| 99久久亚洲精品无码毛片 | 亚洲av中文无码| 久久www免费人成看片| 一级片在线免费看| 亚洲第一区二区快射影院| 国产亚洲精品va在线| 狠狠久久永久免费观看| 亚洲欧洲免费视频| 一级毛片a免费播放王色| 亚洲av无码国产综合专区| 亚洲国产成人片在线观看| 夜色阁亚洲一区二区三区| 免费做爰猛烈吃奶摸视频在线观看 | 日韩亚洲国产二区| 中文字幕无码视频手机免费看| 国产精品小视频免费无限app| 亚洲精品无码久久久久牙蜜区| 中文字幕亚洲综合久久2| 久久亚洲国产精品五月天婷| 欧洲精品免费一区二区三区| 国产精品久久久久久久久免费| 中文字幕免费不卡二区| 中文字幕免费在线看线人动作大片| 亚洲爆乳少妇无码激情| 亚洲国产精品综合久久久|