Android 存儲優化系列專題
- SharedPreferences 系列
《Android 之不要濫用 SharedPreferences》
《Android 之不要濫用 SharedPreferences(2)— 數據丟失》
- ContentProvider 系列(待更)
《Android 存儲選項之 ContentProvider 啟動過程源碼分析》
《Android 存儲選項之 ContentProvider 深入分析》
- 對象序列化系列
《Android 對象序列化之你不知道的 Serializable》
《Android 對象序列化之 Parcelable 深入分析》
《Android 對象序列化之追求完美的 Serial》
- 數據序列化系列(待更)
《Android 數據序列化之 JSON》
《Android 數據序列化之 Protocol Buffer 使用》
《Android 數據序列化之 Protocol Buffer 源碼分析》
- SQLite 存儲系列
《Android 存儲選項之 SQLiteDatabase 創建過程源碼分析》
《Android 存儲選項之 SQLiteDatabase 源碼分析》
《數據庫連接池 SQLiteConnectionPool 源碼分析》
《SQLiteDatabase 啟用事務源碼分析》
《SQLite 數據庫 WAL 模式工作原理簡介》
《SQLite 數據庫鎖機制與事務簡介》
《SQLite 數據庫優化那些事兒》
在上篇《Android 存儲選項之 SQLiteDatabase 源碼分析》一文為大家介紹了數據庫操作相關源碼。先來簡單回顧下,通過使用 SQLiteDatabase 執行 SQLite 數據庫操作,會根據當前線程創建一個私有的 SQLiteSession,SQLiteSession 保證了同一個句柄在同一時間僅有一個線程在操作,SQLiteSession 通過數據庫連接池 SQLiteConnectionPool 獲取一個數據庫連接 SQLiteConnection。每一個 SQLiteConnection 持有一個數據庫訪問句柄完成數據庫操作任務。
不過關于 SQLiteConnectionPool 中數據庫連接的管理機制并沒有詳細介紹,今天我們就結合源碼詳細分析下 SQLiteConnectionPool 是如何管理數據庫連接的。
SQLiteConnectionPool 主要用于緩存所有的數據庫連接,包括一個主連接和若干條非主連接。注意主連接有且僅有一個(表示可寫的),非主連接數量取決于連接池大小配置(最大值 - 1(1 表示主連接))。
連接的獲取
接下來從 SQLiteSession 開始跟蹤獲取一個數據庫連接的過程,前面也有提到 SQLiteDatabase 通過 ThreadLocal 保證線程私有的 SQLiteSession,從而保證了同一個句柄同一時間僅有一個線程操作。SQLiteSession 中獲取數據庫連接方法如下:
//在SQLiteConnectionPool中申請一個數據庫連接
private void acquireConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
if (mConnection == null) {
assert mConnectionUseCount == 0;
//到連接池獲取一個數據庫連接
mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
cancellationSignal); // might throw
mConnectionFlags = connectionFlags;
}
mConnectionUseCount += 1;
}
SQLiteSession 中持有當前 SQLiteDatabase 的數據庫連接池,通過該連接池嘗試獲取一個可用的數據庫連接。
先看下 SQLiteSession 的構造方法,如下:
public SQLiteSession(SQLiteConnectionPool connectionPool) {
if (connectionPool == null) {
throw new IllegalArgumentException("connectionPool must not be null");
}
//持有當前SQLiteDatabase的數據庫連接池
mConnectionPool = connectionPool;
}
SQLiteConnectionPool 中嘗試獲取一個數據庫連接的過程如下:
public SQLiteConnection acquireConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
//獲取一個SQLiteConnection, connectionFlags如果在主線程是 0101 否則 0001
SQLiteConnection con = waitForConnection(sql, connectionFlags, cancellationSignal);
synchronized (mLock) {
if (mIdleConnectionHandler != null) {
//移除該連接的超時關閉時間
mIdleConnectionHandler.connectionAcquired(con);
}
}
return con;
}
- 參數說明
- connectionFlags
這里首先說下 connectionFlags 的取值,它表示當前獲取連接的類型,該取值最終是通過 SQLiteDatabase 的 getThreadDefaultConnectionFlags 方法完成,如下:
int getThreadDefaultConnectionFlags(boolean readOnly) {
//如果是查詢操作,此時readOnly就是CONNECTION_FLAG_READ_ONLY
//CONNECTION_FLAG_READ_ONLY == 0001
//CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY == 0010
int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
//如果是主線程
if (isMainThread()) {
//CONNECTION_FLAG_INTERACTIVE == 0100
//此時如果是只讀計算的值為:0001 != 0100 = 0101
//否則:0010 != 0100 = 0110
flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
}
return flags;
}
如果參數 readOnly = true(只讀模式)返回 0001 否則 0010。
另外它還會受到當前是否是在主線程操作的影響,此時 readOnly = true 時 0001 & 0100 = 0101,否則 0010 & 0100 = 0110。
其實它的主要作用就是區分當前操作是讀還是寫數據庫操作(查詢屬于讀數據庫,插入、刪除、更新都屬于寫數據庫操作),這在嘗試獲取數據庫連接時,決定獲取主連接還是非主連接。
- mIdleConnectionHandler 作用
當數據庫連接長時間處于空閑狀態也是對資源的一種浪費,SQLiteConnectionPool 提供了數據庫連接空閑超時關閉機制,簡單點說,就是通過發送延遲消息(允許最大的空閑時間)來釋放超時的空閑連接。
//移除該連接的空閑超時關閉機制
mIdleConnectionHandler.connectionAcquired(con);
回到上面 acquireConnection 方法,調用 waitForConnection 去獲取一個數據庫連接,該方法內容較多,主要劃分為兩部分:
(1) 嘗試立即獲取數據庫連接。
(2) 等待獲取數據庫連接。
后者是當前不存在空閑的數據庫連接,并且連接數量已經達到允許最大值,此時需要按照優先級排隊等待連接釋放。
- 嘗試立即獲取連接
先來看下嘗試立即獲取數據庫連接的過程,如下:
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
//查詢操作時 0001 & 0010 或者 0101(表示主線程) & 0010,此時值為false
//如果是需要寫入操作updata/insert/delete 0010 & 0010 或者 0110(表示主線程) & 0010,此時值為true
final boolean wantPrimaryConnection =
(connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
final ConnectionWaiter waiter;
final int nonce;
synchronized (mLock) {
//如果操作已經關閉的將會拋出異常
throwIfClosedLocked();
// Abort if canceled.
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
// Try to acquire a connection.
SQLiteConnection connection = null;
//如果是查詢操作此時嘗試獲取非主連接
if (!wantPrimaryConnection) {
//如果當前>=最大連接池數量則返回null
connection = tryAcquireNonPrimaryConnectionLocked(
sql, connectionFlags); // might throw
}
if (connection == null) {
//嘗試獲取主連接,如果是寫操作,默認是需要獲取主連接的
connection = tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
}
if (connection != null) {
return connection;
}
//... 等待獲取過程省略
}
根據 connectionFlags 計算當前需要可讀 / 可寫的數據庫連接,只讀時首先獲取非主連接,否則嘗試獲取主連接。先看下嘗試立即獲取非主連接的過程 tryAcquireNonPrimaryConnectionLocked 方法如下:
//獲取非主連接
private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
String sql, int connectionFlags) {
// Try to acquire the next connection in the queue.
SQLiteConnection connection;
//mAvailableNonPrimaryConnections是緩存所有非主連接的List對象
final int availableCount = mAvailableNonPrimaryConnections.size();
if (availableCount > 1 && sql != null) {
// If we have a choice, then prefer a connection that has the
// prepared statement in its cache.
for (int i = 0; i < availableCount; i++) {
connection = mAvailableNonPrimaryConnections.get(i);
//優先獲取已經執行過該SQL的連接,SQLiteConnection中會
//緩存編譯SQL后的PreparedStatement對象
if (connection.isPreparedStatementInCache(sql)) {
//找到在緩存中移除
mAvailableNonPrimaryConnections.remove(i);
//加入到WeakHashMap緩存
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
}
}
//如果上面沒有根據SQL語句優先獲取到SQLiteConnection
//此時存在空閑連接
if (availableCount > 0) {
// Otherwise, just grab the next one.
//否則就獲取最近緩存的一個
connection = mAvailableNonPrimaryConnections.remove(availableCount - 1);
//加入到WeakHashMap緩存
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
// Expand the pool if needed.
//獲取當前正在執行任務的數量
int openConnections = mAcquiredConnections.size();
if (mAvailablePrimaryConnection != null) {
//如果主連接是空閑的,此時實際連接數量要+1
openConnections += 1;
}
if (openConnections >= mMaxConnectionPoolSize) {
//如果大于允許的最大連接池大小,return null
return null;
}
//否則創建一個新的連接
connection = openConnectionLocked(mConfiguration,
false /*primaryConnection*/); // might throw
//加入到正在使用緩存
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
可以看到優先根據要執行的 SQL 語句獲取對應的 SQLiteConnection 對象,SQLiteConnection 中通過 LRU 默認會緩存執行過的 SQL 語句,默認最大緩存個數為 25,其內部實際緩存的是 SQL 語句編譯后的 PreparedStatement 對象。
如果不能根據 SQL 語句優先獲取到,則默認取出最后一個(availableCount - 1)返回。
如果當前無可復用的連接(availableCount <= 0),此時需要根據當前連接池允許最大數量決定是否要創建一個新的連接返回,否則直接返回 null。后續就要通過等待的方式獲取了。
這里的 finishAcquireConnectionLocked 方法需要說明下作用
private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) {
try {
//需要的數據庫是否是只讀的
final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
//將連接設置為相應操作模式
connection.setOnlyAllowReadOnlyOperations(readOnly);
//將其緩存到WeakHashMap
mAcquiredConnections.put(connection, AcquiredConnectionStatus.NORMAL);
} catch (RuntimeException ex) {
Log.e(TAG, "Failed to prepare acquired connection for session, closing it: "
+ connection + ", connectionFlags=" + connectionFlags);
closeConnectionAndLogExceptionsLocked(connection);
throw ex; // rethrow!
}
}
mAcquiredConnections 是 WeakHashMap 容器,主要緩存當前正在執行任務的數據庫連接。判斷當前正在執行操作連接(SQLiteConnection)的數量。
接下來看下立即獲取主連接的過程,主連接的獲取與非主連接有一定區別,一起來看下:
//嘗試立即獲取數據庫主連接
private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
//主連接是直接作為成員緩存在SQLiteConnectionPool中
SQLiteConnection connection = mAvailablePrimaryConnection;
if (connection != null) {
//將其在SQLiteConnectionPool中持有置為null,表示當前無空閑主連接
mAvailablePrimaryConnection = null;
//標志正在執行任務的連接
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
// Make sure that the primary connection actually exists and has just been acquired.
for (SQLiteConnection acquiredConnection : mAcquiredConnections.keySet()) {
//遍歷標志正在執行任務的容器,判斷如果存在主連接,表示當前有一條主連接正在工作
if (acquiredConnection.isPrimaryConnection()) {
//此時直接返回null,一個SQLiteConnectionPool僅能有一個主連接。
return null;
}
}
//走到這里說明既沒有空閑的主連接,也沒有正在執行任務的主連接
//此時直接創建一個主連接。
connection = openConnectionLocked(mConfiguration,
true /*primaryConnection*/); // might throw
//加入到執行標志
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
前面我們有多次提到,每一個數據庫連接池有且僅有一條數據庫主連接,主連接表示可寫的。關于這部分如果想進一步了解可以參考下一篇《Android 存儲選項之 SQLite 優化那些事兒》會做進一步介紹。
mAvailablePrimaryConnection 表示當前數據庫主連接,它直接作為成員在 SQLiteConnectionPool 中,根據其是否為 null 判斷當前是否存在空閑的主連接,如果不存在則表示主連接正在執行任務(也可能因為空閑超時已經被回收),所以 for 循環就是判斷當前是否存在正在工作的主連接。
如果不存在正在執行任務的主連接和不存在空閑的主連接(已經被空閑超時回收),此時需要創建一條數據庫主連接并返回。
至此關于數據庫連接的立即獲取就已經分析完了,整個過程相對還是比較容易理解的。不過它們的返回都有可能為 null,此時表示連接數量已達到最大值,這個時候就需用等待其它連接釋放。重新回到 waitForConnection 方法看下等待獲取連接過程。
- 等待獲取連接
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
// 立即獲取連接過程上面已經做了分析,這里直接省略
//優先級的獲取跟當前操作是否在主線程有關
//如果是在主線程返回1,否則 0
final int priority = getPriority(connectionFlags);
//當前時間
final long startTime = SystemClock.uptimeMillis();
//waiter是一個ConnectionWaiter,是一個單向鏈表結構,存在一個同類的mNext成員
//obtainConnectionWaiterLocked會去復用(取鏈表頭)或者新建一個
waiter = obtainConnectionWaiterLocked(Thread.currentThread(), startTime,
priority, wantPrimaryConnection, sql, connectionFlags);
ConnectionWaiter predecessor = null;
//當前等待獲取數據庫連接池隊列
ConnectionWaiter successor = mConnectionWaiterQueue;
while (successor != null) {
//按照順序調整當前等待隊列
if (priority > successor.mPriority) {
//如果當前優先級 > mConnectionWaiterQueue的優先級
waiter.mNext = successor;
break;
}
//遍歷查找當前等待隊列第一個小于當前優先級的位置
predecessor = successor;
successor = successor.mNext;
}
if (predecessor != null) {
//此時說明所有等待鎖的優先級都不小于當前等待鎖的priority
//此時當前等待鎖為等待隊列的最后一個
predecessor.mNext = waiter;
} else {
//此時說明當前等待鎖為最高優先級
//當前等待鎖為等待隊列的第一個
mConnectionWaiterQueue = waiter;
}
nonce = waiter.mNonce;
}
//鎖Lock結束
// Set up the cancellation listener.
if (cancellationSignal != null) {
cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
@Override
public void onCancel() {
synchronized (mLock) {
if (waiter.mNonce == nonce) {
cancelConnectionWaiterLocked(waiter);
}
}
}
});
}
try {
// Park the thread until a connection is assigned or the pool is closed.
// Rethrow an exception from the wait, if we got one.
// CONNECTION_POOL_BUSY_MILLIS默認32s
long busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
long nextBusyTimeoutTime = waiter.mStartTime + busyTimeoutMillis;
for (; ; ) { //等待開始
// Detect and recover from connection leaks.
if (mConnectionLeaked.compareAndSet(true, false)) {
//處理已經處于泄漏狀態的數據庫連接(沒有任何引用持有)
synchronized (mLock) {
wakeConnectionWaitersLocked();
}
}
// 使當前線程進入休眠,最長時間:busyTimeoutMillis * 1000000L,單位納秒
// 注意喚醒操作是在 releaseConnection 方法,此時有連接被釋放,會喚醒該休眠
// Wait to be unparked (may already have happened), a timeout, or interruption.
LockSupport.parkNanos(this, busyTimeoutMillis * 1000000L);
// Clear the interrupted flag, just in case.
Thread.interrupted();
// Check whether we are done waiting yet.
synchronized (mLock) {
throwIfClosedLocked();
// 在releaseConnection方法,如果有新的連接被釋放
// 此時按照等待隊列的優先級獲取到需要的數據庫連接
final SQLiteConnection connection = waiter.mAssignedConnection;
final RuntimeException ex = waiter.mException;
if (connection != null || ex != null) {
//重新回收該等待鎖:ConnectionWaiter
recycleConnectionWaiterLocked(waiter);
if (connection != null) {
//當前等待鎖獲取到數據庫連接,直接返回
return connection;
}
throw ex; // rethrow!
}
//重新調整busyTimeoutMills
final long now = SystemClock.uptimeMillis();
if (now < nextBusyTimeoutTime) {
busyTimeoutMillis = now - nextBusyTimeoutTime;
} else {
logConnectionPoolBusyLocked(now - waiter.mStartTime, connectionFlags);
busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
nextBusyTimeoutTime = now + busyTimeoutMillis;
}
}
}
} finally {
// Remove the cancellation listener.
if (cancellationSignal != null) {
cancellationSignal.setOnCancelListener(null);
}
}
}
雖然等待獲取連接的代碼有點多,但是并不難理解,先來看 obtainConnectionWaiterLocked 方法獲取一個數據庫連接等待鎖:
private ConnectionWaiter obtainConnectionWaiterLocked(Thread thread, long startTime,
int priority, boolean wantPrimaryConnection, String sql, int connectionFlags) {
ConnectionWaiter waiter = mConnectionWaiterPool;
if (waiter != null) {
//指向它的下一個,當前的被占用了
mConnectionWaiterPool = waiter.mNext;
waiter.mNext = null;
} else {
//否則創建
waiter = new ConnectionWaiter();
}
//當前線程
waiter.mThread = thread;
//等待開始時間
waiter.mStartTime = startTime;
//等待優先級
waiter.mPriority = priority;
//是否需要主連接
waiter.mWantPrimaryConnection = wantPrimaryConnection;
//SQL語句
waiter.mSql = sql;
//需要的連接類型
waiter.mConnectionFlags = connectionFlags;
return waiter;
}
ConnectionWaiter 表示當前線程等待獲取數據庫連接的鎖對象,每一個等待獲取數據庫連接的線程都會關聯一個 ConnectionWatier,當有連接被釋放時,根據等待優先級對應的 ConnectionWaiter 獲取到數據庫連接,此時即可返回完成數據庫訪問操作。
ConnectionWaiter 是個單向鏈表結構,而且 SQLiteConnectionPool 內部還對其進行復用:
//回收ConnectionWaiter
private void recycleConnectionWaiterLocked(ConnectionWaiter waiter) {
waiter.mNext = mConnectionWaiterPool;
waiter.mThread = null;
waiter.mSql = null;
waiter.mAssignedConnection = null;
waiter.mException = null;
waiter.mNonce += 1;
mConnectionWaiterPool = waiter;
}
等待的優先級 getPriority 方法與數據庫操作類型無關(讀/寫操作),與當前操作的線程有關,該值只有 0/1 兩種情況。
/**
* CONNECTION_FLAG_INTERACTIVE == 0100
* 如果查詢操作:0001 & 0100 或 0101(主線程) & 0100,如果是在主線程此時返回 1,否則 0
* 寫操作:0010 & 0100 或 0110(主線程) & 0100,如果是主線程此時返回 1,否則 0
* */
private static int getPriority(int connectionFlags) {
return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
}
死循環 for(; ; ) 一直等待有數據庫連接被釋放,LockSupport.parkNanos 會使當前等待線程進入 wait 狀態,如果有新的連接被釋放,會根據 ConnectionWaiter 喚醒對應的等待線程,該處理機制可以說是非常高效的。能夠有效解決等待獲取連接時間長短的問題。
連接的釋放
連接的釋放就是把執行完任務的連接緩存到數據庫連接池(實際是 List 集合),并進行 unpark 通知正在等待獲取連接的線程。如何判斷當前連接任務已經執行完了呢?
//在SQLiteSession中執行任務時acquireConnection獲取數據庫連接
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
return mConnection.executeForCursorWindow(sql, bindArgs,
window, startPos, requiredPos, countAllRows,
cancellationSignal); // might throw
} finally {
//任務執行完成一定釋放該數據庫連接
releaseConnection(); // might throw
}
釋放數據庫連接操作最終調用到 SQLiteConnectionPool 中:
public void releaseConnection(SQLiteConnection connection) {
synchronized (mLock) {
if (mIdleConnectionHandler != null) {
//重新加入超時關閉機制
mIdleConnectionHandler.connectionReleased(connection);
}
//移除正在執行任務的標志, 前面已經分析過mAcquiredConnections是WeakHashMap
AcquiredConnectionStatus status = mAcquiredConnections.remove(connection);
if (status == null) {
throw new IllegalStateException("Cannot perform this operation "
+ "because the specified connection was not acquired "
+ "from this pool or has already been released.");
}
if (!mIsOpen) {
//如果數據庫已經關閉,此時直接關閉該連接
closeConnectionAndLogExceptionsLocked(connection);
} else if (connection.isPrimaryConnection()) { //如果是主鏈接
//如果連接狀態不是要關閉
if (recycleConnectionLocked(connection, status)) {
assert mAvailablePrimaryConnection == null;
//mAvailablePrimaryConnection == null,賦值給其成員
mAvailablePrimaryConnection = connection;
}
//喚醒等待獲取連接的線程
wakeConnectionWaitersLocked();
} else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
//如果是非主連接,此時緩存數量已經大于允許的最大連接數量直接關閉,-1是考慮主連接
closeConnectionAndLogExceptionsLocked(connection);
} else {
if (recycleConnectionLocked(connection, status)) {
//加入到緩存池(List集合)
mAvailableNonPrimaryConnections.add(connection);
}
//喚醒等待獲取連接的線程
wakeConnectionWaitersLocked();
}
}
}
if(!mIsOpen) 表示當前連接池已經被關閉了,此時要直接關閉釋放的數據庫連接。否則根據連接的類型(主連接 / 非主連接)將其加入對應的緩存。根據當前等待隊列的優先級
并喚醒等待獲取連接的線程 wakeConnectionWaitersLocked 方法如下:
private void wakeConnectionWaitersLocked() {
// Unpark all waiters that have requests that we can fulfill.
// This method is designed to not throw runtime exceptions, although we might send
// a waiter an exception for it to rethrow.
ConnectionWaiter predecessor = null;
ConnectionWaiter waiter = mConnectionWaiterQueue;
boolean primaryConnectionNotAvailable = false;
boolean nonPrimaryConnectionNotAvailable = false;
while (waiter != null) {
boolean unpark = false;
if (!mIsOpen) {
//數據庫已經關閉
unpark = true;
} else {
try {
SQLiteConnection connection = null;
if (!waiter.mWantPrimaryConnection && !nonPrimaryConnectionNotAvailable) {
//嘗試獲取非主連接
connection = tryAcquireNonPrimaryConnectionLocked(
waiter.mSql, waiter.mConnectionFlags); // might throw
if (connection == null) {
//表示當前獲取不到非主連接
nonPrimaryConnectionNotAvailable = true;
}
}
if (connection == null && !primaryConnectionNotAvailable) {
//嘗試獲取主連接
connection = tryAcquirePrimaryConnectionLocked(
waiter.mConnectionFlags); // might throw
if (connection == null) {
//此時表示主連接也獲取不到
primaryConnectionNotAvailable = true;
}
}
if (connection != null) {
//獲取到連接
waiter.mAssignedConnection = connection;
unpark = true;
} else if (nonPrimaryConnectionNotAvailable && primaryConnectionNotAvailable) {
//如果獲取不到連接就到此為止吧
//此時waitForConnection仍然繼續等待
break;
}
} catch (RuntimeException ex) {
// Let the waiter handle the exception from acquiring a connection.
waiter.mException = ex;
unpark = true;
}
}
final ConnectionWaiter successor = waiter.mNext;
if (unpark) {
//
if (predecessor != null) {
predecessor.mNext = successor;
} else {
mConnectionWaiterQueue = successor;
}
waiter.mNext = null;
//喚醒等待獲取連接線程
LockSupport.unpark(waiter.mThread);
} else {
predecessor = waiter;
}
waiter = successor;
}
}
mConnectionWaiterQueue 為當前等待隊列,根據當前需要的連接類型依次嘗試獲取非主連接或主連接。unpark 表示當前是否獲取到連接,如果仍然未獲取到,此時 waitForConnection 將繼續等待。否則方法的最后喚醒相應的等待線程,并重新調整當前等待隊列。
LockSupport.unpark(watier.mThread)
總結
為了進一步提高并發性能,我們還可以打開 WAL(Write-Ahead-Logging)模式。WAL 模式會將修改的數據單獨寫到一個 WAL 文件中,同時也會引入了 WAL 日志文件鎖。通過 WAL 模式讀和寫可以完全地并發執行,不會互相阻塞。
總的來說通過連接池與 WAL 模式,我們可以很大程度上增加 SQLite 的讀寫并發,大大減少由于并發導致的等待耗時,錦衣大家在應用中可以嘗試開啟。
以上便是個人在學習 SQLiteConnectionPool 時的心得和體會,文中如有不妥或更好的分析結果,還請大家指出!
文章如果對你有幫助,就請留個贊吧!