litepal是郭林封装的一个简单的数据库工具,内部使用的数据库是通过单例的方式维护的,如果同一个应用存在多个数据库的话,需要先把之前那个数据库关闭再重新打开另一个数据库。
问题的由来
问题的出现是因为我在做聊天sdk代码重构的时候,发现如果游戏接入了客服和聊天两个sdk,目前使用的又是不同的数据库,都接入之后就可能会出现同时使用,则就会在不同数据库中频繁切换,且都在各自子线程中分别保存数据,则就会出现线程不安全的问题。
问题所在(多线程问题)
深入查看内部数据操作的源码之后,发现内部的数据操作都是有加锁的,但是切换数据库之后马上操作数据,这是两个步骤,在多线程的处理中,必然会出现在保存数据的时候,该表并不一定在自己所在的数据库,找不到表。
解决方案一:加锁
所以第一个解决方案就出现了,将切换数据库和操作数据一起加锁,将两步当成原子操作,这样就解决了,但是测试发现这样性能大大的下降了,之前5000条数据的插入,需要10秒多一点,两个线程一起各自插入5000条数据,各自用的时间达到了251秒,这个方案就被直接否定了。
解决方案二:多个数据库共存
之后又看源码发现主要弊端还是因为在同一时间只存在一个数据库,如果能各自使用各自的数据库,不去切换不就没有任何冲突了么。
最终查看源码知道,获取数据库是通过Connector.getDatabase()这个方法获得的,而如果在增删改查之前我不使用他的这个方法获得数据库,而是传一个改表对应的数据库给他不就可以了,LitePalSupport类提供数据增删改查的方法,继承它并重写对应方法,使用数据库时传一个对应的数据库即可。
现在的问题就转换成了如何获取到对应的数据库?
这时候对于源码比较熟悉了,我发现可以通过先切换到指定的数据库,然后通过手动调用Connector.getDatabase()就能获得改数据库了。
而这之后还有一个小问题,那就是获得第一个数据库没有问题,获取第二个数据库的时候也是先切换数据库,这时候源码里是会把之前那个数据库给关闭,再在Connector.getDatabase()时创建新的数据库。
找到关闭数据库的代码,发现如果当前数据库不为空就会先关闭再设置为null,这时候解决办法就跃然纸上了。我在获取完数据库之后,通过反射的方法手动设置为null,那么在第二次设置的时候发现是null,则关不了第一个数据库了,到这里两个数据库就能共存了,也不再切换数据库,最终完美的解决了这个问题。
网友评论