Android 数据库之 SQLiteConnectionPool 源码分析

闪存
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;
}
  • 参数说明
  1. 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。

其实它的主要作用就是区分当前操作是读还是写数据库操作(查询属于读数据库,插入、删除、更新都属于写数据库操作),这在尝试获取数据库连接时,决定获取主连接还是非主连接。

  1. 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 时的心得和体会,文中如有不妥或更好的分析结果,还请大家指出!

文章如果对你有帮助,就请留个赞吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345