從零開始實現Java多線程數據庫連接池(附一個神秘的問題)_第1頁
從零開始實現Java多線程數據庫連接池(附一個神秘的問題)_第2頁
從零開始實現Java多線程數據庫連接池(附一個神秘的問題)_第3頁
從零開始實現Java多線程數據庫連接池(附一個神秘的問題)_第4頁
從零開始實現Java多線程數據庫連接池(附一個神秘的問題)_第5頁
已閱讀5頁,還剩9頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、從零開始實現Java多線程數據庫連接池(附一個神秘的問題)本例采用mysql數據庫,因此請先下載mysql-connectionjar在我們的實際開發中,離不開和數據庫打交道。而和數據庫的通信,離不開數據庫連接。通常用JDBC連接數據庫時,需要加載數據驅動,然后再通過接口返回數據庫連接。一般分為兩步:1、加載驅動至內存Class.forName(“com.mysql.jdbc.Driver”);2、創建并獲取連接,返回的是JDBC中的ConnectionDriverManager.getConnection(url,user,password)示例:要連接的數據庫URLStringurl=jd

2、bc:mysql:/localhost:3306/tangwmdb;連接的數據庫時使用的用戶名Stringusername=root;連接的數據庫時使用的密碼Stringpassword=root;/1加載驅動DriverManager.registerDriver(newcom.mysql.jdbc.Driver();不推薦使用這種方式來加載驅動Class.forName(com.mysql.jdbc.Driver);推薦使用這種方式來加載驅動/2獲取與數據庫的鏈接Connectionconn=DriverManager.getConnection(url,username,password

3、);/3.獲取用于向數據庫發送sql語句的statementStatementst=conn.createStatement();Stringsql=selectid,name,passwordfrommembers;/4.向數據庫發sql,并獲取代表結果集的resultsetResultSetrs=st.executeQuery(sql);/5.取出結果集的數據while(rs.next()System.out.println(id=+rs.getObject(id);System.out.println(name=+rs.getObject(name);System.out.println

4、(password=+rs.getObject(password);/6.關閉鏈接,釋放資源rs.close();st.close();conn.close();眾所周知,創建數據庫連接需要消耗較多的資源,且創建時間也較長。如果網站一天100萬PV(假設每個頁面都有DB讀取或修改操作),程序就需要創建100萬次連接,極大的浪費資源。事實上,同一時間需要創建數據庫連接的請求數量并不多,一般幾百個足夠了。那么我們可以根據需要創建一個連接池,它負責分配、管理和釋放數據庫連接,它允許應用程序重復使用同一個現有的數據庫連接,而不是重新建立一個。這里用到了設計模式中的一個模式:享元模式(Flyweight

5、)。比如我們的連接池中有1000條連接,請求來時,連接池從池中分配一條給請求,用完后收回,而不是銷毀,等到下次有請求來時,又可以重復分配使用。應用樹f1r應用歸+VnnectonnecLion.rnecLion;連接迪為諳求魅中玉際E勺讎圭分虱_魯二于當前總用當使用了數據庫連接池之后,在項目的實際開發中就不需要編寫連接數據庫的代碼了;接從數據源獲得數據庫的連接。比如:/DBCP數據庫連接池DataSourceds=BasicDataSourceFactory.createDataSource(prop);Connectionconn=ds.getConnection();可以看到創建連接的工作

6、很簡單,因為復雜的分配、回收功能都交給了連接池去處理。當前有一些開源的數據連接池實現:DBCP數據庫連接池C3P0數據庫連接池另外阿里開源項目Druid(整個項目由數據庫連接池、插件框架和SQL解析器組成)中的數據庫連接池被很多互聯網公司都采用在生產環境中。編寫自己的數據庫連接池編寫的連接池需要做到以下幾個基本點:1、可配置并管理多個連接節點的連接池nodenajne=defa口It;,testdb設罡數據庫連妾節點default.driver=oom.mysqljdbcDrivrdefaultirl=jdbc:mysql:/lozalhost:3305/tangweniungdbdefaul

7、t.iser=iLOotdefault.psssword=rcatdefault.umjccomnectian.s=10defaultminccntiiecticns=2default,initcon.necticns=2default.default.imEOirt=50D0testdbdriver=co(n.ny=ql.jdbz.Driveetestdb:Z/7:3306/t=stestdbuser=r3OT;testdbpasssctr-d=ract2testdb.maxccnnecticn=LC1testdbmirtccnrieaticn=2testdb1mitcomnection.s

8、=22、始使化時根據配置中的初始連接數創建指定數量的連接3、在連接池沒有達到最大連接數之前,如果有可用的空閑連接就直接使用空閑連接,如果沒有,就創建新的連接。4、當連接池中的活動連接數達到最大連接數,新的請求進入等待狀態,直到有連接被釋放。5、由于數據庫連接閑置久了會超時關閉,因此需要連接池采用機制保證每次請求的連接都是有效可用的。6、線程安全7、連接池內部要保證指定最小連接數量的空閑連接。對于最小連接數在實際應用中的效果以及與初始連接數的區別,其實理解的不是很透。在程序中我采用的方式是,如動連接數+空閑連接數最小連接數,就補齊對應數量最小連接數-活動連接數-空閑連接數)的空閑連接摘錄一段:數

9、據庫連接池的最小連接數和最大連接數的設置要考慮到以下幾個因素:最小連接數:是連接池一直保持的數據庫連接,所以如果應用程序對數據庫連接的使用量不大,將會有大量的數據庫連接資源被浪費。最大連接數:是連接池能申請的最大連接數,如果數據庫連接請求超過次數,后面的數據庫連接請求將被加入到等待隊列中,這會影響以后的數據庫操作。如果最小連接數與最大連接數相差很大,那么最先連接請求將會獲利,之后超過最小連接數量的連接請求等價于建立一個新的數據庫連接。不過,這些大于最小連接數的數據庫連接在使用完不會馬上被釋放,它將被放到連接池中等待重復使用或是超時后被釋放。系統結構:1連接池接口IConnectionPool:

10、里面定義一些基本的獲取連接的一些方法。2.連接池接口實現ConnectionPool3連接池管理DBConnectionManager:管理不同的連接池,所有的連接都是通過這里獲得。4.其它工具類,諸如屬性讀取類PropertiesManager,屬性保存類DBPropertyBean。連掛泄管理:nBctnPlMaaagerKDrrectcrpoQls4gEtInstanoeOHnitfl麼頂連多:gstCDTrecta-C荒訐連多:El空連多迪:+最門連騒:ntj+從豔址夢51生倍:getGcrnectionO+莪取耳前寥逹的淫援:圭+創崖新曲連毘:nsflCannednQ廨啟呂前連溟:抵

11、伽町請越蹇述需精睡看:destrjyO我戲接池靚:工應啊J:.4TDEConnBczionP&o鑑src止舟com.ttvm.TDB匚crinE&icmPchol丄EConnc-ctionMnager.jvaConnectionPdcl.js3-|T|DBPropeirtyBean.java!:工T匚cmnectionPoolja/a*曲cam.hvm.TDBCannGctionPaol.runI:-.fteEool.java-mcom.hMTn.TDBCannectionPaol.LftilI:-TPiropertiesManagjawa丿已ConfigsDB.prDp&rtiM國Ipert

12、ies雖JRJELibraryJa-jaSE-l.Tk.HIccNl;T17_n.klAfA01Ti工程代碼:DBPropertyBean.javapackagecom.twm.TDBConnectionPool;publicclassDBPropertyBeanprivateStringnodeName;數據連接驅動privateStringdriverName;數據連接urlprivateStringurl;數據連接usernameprivateStringusername;數據連接密碼privateStringpassword;連接池最大連接數privateintmaxConnectio

13、ns;連接池最小連接數privateintminConnections;連接池初始連接數privateintinitConnections;重連間隔時間,單位毫秒privateintconninterval;獲取連接超時時間,單位毫秒,0永不超時privateinttimeout;構造方法publicDBPropertyBean()super();下面是getterandsetter/*獲取數據庫連接節點名稱return*/publicStringgetNodeName()returnnodeName;/*設置數據庫連接節點名稱paramnodeName*/publicvoidsetNodeN

14、ame(StringnodeName)this.nodeName=nodeName;/*獲取數據庫驅動return*/publicStringgetDriverName()returndriverName;/*設置數據庫驅動*paramdriverName*/publicvoidsetDriverName(StringdriverName)this.driverName=driverName;/*/*獲取數據庫urlreturn*/publicStringgetUrl()returnurl;/*設置數據庫urlparamurl*/publicvoidsetUrl(Stringurl)this.

15、url=url;/*獲取用戶名return*/publicStringgetUsername()returnusername;/*設置用戶名paramusername*/publicvoidsetUsername(Stringusername)this.username=username;/*獲取數據庫連接密碼return*/publicStringgetPassword()returnpassword;/*設置數據庫連接密碼parampassword*/publicvoidsetPassword(Stringpassword)this.password=password;/*獲取最大連接數r

16、eturn*/publicintgetMaxConnections()returnmaxConnections;/*設置最大連接數parammaxConnections*/publicvoidsetMaxConnections(intmaxConnections)this.maxConnections=maxConnections;/*獲取最小連接數(也是數據池初始連接數)*獲取最小連接數(也是數據池初始連接數)return*/publicintgetMinConnections()returnminConnections;/*設置最小連接數(也是數據池初始連接數)paramminConnec

17、tions*/publicvoidsetMinConnections(intminConnections)this.minConnections=minConnections;/*獲取初始加接數return*/publicintgetInitConnections()returninitConnections;/*設置初始連接數paraminitConnections*/publicvoidsetInitConnections(intinitConnections)this.initConnections=initConnections;/*獲取重連間隔時間,單位毫秒return*/publi

18、cintgetConninterval()returnconninterval;/*設置重連間隔時間,單位毫秒paramconninterval*/publicvoidsetConninterval(intconninterval)this.conninterval=conninterval;/*獲取連接超時時間,單位毫秒return*/publicintgetTimeout()returntimeout;/*設置連接超時時間,單位毫秒,0-無限重連paramtimeout*/publicvoidsetTimeout(inttimeout)this.timeout=timeout;IConne

19、ctionPool.javapackagecom.twm.TDBConnectionPool;importjava.sql.Connection;importjava.sql.SQLException;publicinterfaceIConnectionPool/*獲取一個數據庫連接,如果等待超過超時時間,將返回nullreturn數據庫連接對象*/publicConnectiongetConnection();/*獲得當前線程的連接庫連接return數據庫連接對象*/publicConnectiongetCurrentConnecton();/*釋放當前線程數據庫連接paramconn數據庫

20、連接對象throwsSQLException*/publicvoidreleaseConn(Connectionconn)throwsSQLException;/*銷毀清空當前連接池*/publicvoiddestroy();/*連接池可用狀態return連接池是否可用*/publicbooleanisActive();/*定時器,檢查連接池*/publicvoidcheckPool();/*獲取線程池活動連接數return線程池活動連接數*/publicintgetActiveNum();/*獲取線程池空閑連接數return線程池空閑連接數*/publicintgetFreeNum();Co

21、nnectionPool.javapackagecom.twm.TDBConnectionPool;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;importjava.sql.Connection;importjava.sql.Driver;importjava.sql.Driver;importjava.sql.DriverManager;importjava.sql.SQLException;importjava.util.Link

22、edList;importjava.util.List;importjava.util.TimerTask;importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurrent.TimeUnit;importorg.apache.log4j.Logger;/*類說明:友元類,包內可見,不提供給客戶程序直接訪問。*/classConnectionPoolimplementsIConnectionPoolprivatestaticfina

23、lLoggerlog=Logger.getLogger(ConnectionPool.class);privateDBPropertyBeanpropertyBean=null;連接池可用狀態privateBooleanisActive=true;/空閑連接池。由于List讀寫頻繁,使用LinkedList存儲比較合適privateLinkedListfreeConnections=newLinkedList();/活動連接池。活動連接數=允許最大連接數(maxConnections)privateLinkedListactiveConnections=newLinkedList();當前線程

24、獲得的連接privateThreadLocalcurrentConnection=newThreadLocal();構造方法無法返回null,所以取消掉。在下面增加了CreateConnectionPoo靜態方法。privateConnectionPool()super();publicstaticConnectionPoolCreateConnectionPool(DBPropertyBeanpropertyBean)ConnectionPoolconnpool=newConnectionPool();pertyBean=propertyBean;加載驅動在多節點環境配置下,因為在這里無法判

25、斷驅動是否已經加載可能會造成多次重復加載相同驅動。因此加載驅動的動作,挪到connectionManager管理類中去實現了。/*tryClass.forName(pertyBean.getDriverName();(加載JDBC驅動+pertyBean.getDriverName()+成功”);catch(ClassNotFoundExceptione)(未找到JDBC驅動+pertyBean.getDriverName()+,請引入相關包);returnnull;*/基本點2、始使化時根據配置中的初始連接數創建指定數量的連接for(inti=0;ipertyBean.getInitConn

26、ections();i+)tryConnectionconn=connpool.NewConnection();connpool.freeConnections.add(conn);catch(SQLException|ClassNotFoundExceptione)log.error(pertyBean.getNodeName()+節點連接池初始化失敗);returnnull;connpool.isActive=true;connpool.isActive=true;returnconnpool;/*檢測連接是否有效param數據庫連接對象returnBoolean*/privateBool

27、eanisValidConnection(Connectionconn)throwsSQLExceptiontryif(conn=null|conn.isClosed()returnfalse;catch(SQLExceptione)thrownewSQLException(e);returntrue;/*創建一個新的連接return數據庫連接對象throwsClassNotFoundExceptionthrowsSQLException*/privateConnectionNewConnection()throwsClassNotFoundException,SQLExceptionConn

28、ectionconn=null;tryif(pertyBean!=null)Class.forName(pertyBean.getDriverName();conn=DriverManager.getConnection(pertyBean.getUrl(),pertyBean.getUsername(),pertyBean.getPassword();catch(SQLExceptione)thrownewSQLException(e);returnconn;OverridepublicsynchronizedConnectiongetConnection()Connectionconn=n

29、ull;if(this.getActiveNum()0)/分支1.1:如果空閑池中有連接,就從空閑池中直接獲取(分支1.1:如果空閑池中有連接,就從空閑池中直接獲取);conn=this.freeConnections.pollFirst();連接閑置久了也會超時,因此空閑池中的有效連接會越來越少,需要另一個進程進行掃描監測,不斷保持一定數量的可用連接。在下面定義了checkFreepools的TimerTask類,在checkPool()方法中進行調用。基本點5、由于數據庫連接閑置久了會超時關閉,因此需要連接池采用機制保證每次請求的連接都是有效可用的。tryif(this.isValidCo

30、nnection(conn)if(this.isValidConnection(conn)this.activeConnections.add(conn);currentConnection.set(conn);elseconn=getConnection();同步方法是可重入鎖catch(SQLExceptione)e.printStackTrace();else/分支1.2:如果空閑池中無可用連接,就創建新的連接(分支1.2:如果空閑池中無可用連接,就創建新的連接,);tryconn=this.NewConnection();this.activeConnections.add(conn)

31、;catch(ClassNotFoundException|SQLExceptione)e.printStackTrace();else/分支2:當前已到達最大連接數/基本點4、當連接池中的活動連接數達到最大連接數,新的請求進入等待狀態,直到有連接被釋放。(分支2:當前已到達最大連接數);longstartTime=System.currentTimeMillis();進入等待狀態。等待被notify(),notifyALL()喚醒或者超時自動蘇醒trythis.wait(pertyBean.getConninterval();catch(InterruptedExceptione)log.e

32、rror(線程等待被打斷);若線程超時前被喚醒并成功獲取連接,就不會走到returnnull。若線程超時前沒有獲取連接,則返回null。如果timeout設置為0,就無限重連。if(pertyBean.getTimeout()!=0)if(System.currentTimeMillis()-startTimepertyBean.getTimeout()returnnull;conn=this.getConnection();returnconn;OverridepublicConnectiongetCurrentConnecton()Connectionconn=currentConnect

33、ion.get();tryif(!isValidConnection(conn)conn=this.getConnection();catch(SQLExceptione)e.printStackTrace();returnconn;OverridepublicsynchronizedvoidreleaseConn(Connectionconn)throwsSQLException(Thread.currentThread().getName()+關閉連接:activeConnections.remove:+conn);this.activeConnections.remove(conn);t

34、his.currentConnection.remove();this.currentConnection.remove();活動連接池刪除的連接,相應的加到空閑連接池中tryif(isValidConnection(conn)freeConnections.add(conn);elsefreeConnections.add(this.NewConnection();catch(ClassNotFoundException|SQLExceptione)e.printStackTrace();喚醒getConnection()中等待的線程this.notifyAll();Overridepubl

35、icsynchronizedvoiddestroy()for(Connectionconn:this.freeConnections)tryif(this.isValidConnection(conn)conn.close();catch(SQLExceptione)e.printStackTrace();for(Connectionconn:this.activeConnections)tryif(this.isValidConnection(conn)conn.close();catch(SQLExceptione)e.printStackTrace();this.isActive=fal

36、se;this.freeConnections.clear();this.activeConnections.clear();OverridepublicbooleanisActive()returnthis.isActive;OverridepublicvoidcheckPool()finalStringnodename=pertyBean.getNodeName();ScheduledExecutorServiceses=Executors.newScheduledThreadPool(2);功能一:開啟一個定時器線程輸出狀態ses.scheduleAtFixedRate(newTimerTask()Overridepub

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論