SQLite在多线程并发访问的应用

SQLite在多线程并发访问的应用

最近遇到个SQLite的问题把我卡住了小半天,最后总结一句话:SQLite不支持多线程

研究一下,发现有以下2种方案可行

1.首先当多个线程并发操作同一个数据库,同时存在insert、delete和select操作,数据是不安全的,在Android内置的SQLite中也是不允许的,这时会造成冲突异常。不允许多线程,则必须实现多线程同步

多线程同步锁的访问SQLite使用:始终让整个Application保持一个database连接,这样的话即使多线程同时访问sqlite,database对象使用java锁会保持访问的序列化。我们一般都是用SQLHelper来管理数据库,而一个Helper实例会产生一个database连接,所以我们只需要让整个Application产生一个SQLHelper的实例就行了

public class SQLHelper extends SQLiteOpenHelper {

	private static final int VERSION_CODE = 1;
	private static SQLHelper helper;

	/**
	 * 同步锁 +单例模式获得唯一数据库帮助类实例
	 * 
	 * @param context
	 * @return
	 */
	public synchronized static SQLHelper getInstance(Context context) {
		if (helper == null) {
			helper = new SQLHelper(context);
		}
		return helper;
	}

	private SQLHelper(Context context) {
		super(context, "person.db", null, VERSION_CODE);
	}

	private SQLHelper(Context context, String name, CursorFactory factory,
			int version) {
		super(context, name, factory, VERSION_CODE);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		Log.i("sqlite", "数据库被创建");
		// 创建数据库表
		String sql = "create table t_student(_id integer primary key autoincrement,id integer,name varchr(20),age int,address varchr(40))";
		db.execSQL(sql);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		String sql = "drop table t_student";
		db.execSQL(sql);
	}

	@Override
	public synchronized void close() {
		super.close();
	}
}


在程序的入口处,Application中产生一个SQLHelper的实例,注意此处的onTerminate()不一定每次退出时都能执行,建议在首页(主activity)的onDestory()方法中调用dbHelper.close();来关闭数据库连接

public class MyApplication extends Application {
    private Context context;
    private DBHelper dbHelper;

    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
        dbHelper = DBHelper.getInstance(context);
    }

    public void onTerminate() {
        super.onTerminate();
        dbHelper.close();
    }
}

总结:多线程 同步锁的方式访问SQLite,保证了同一个时刻SQLite 只有一个连接,多个线程排队依次访问数据库,完美解决了冲突问题。

这个问题确实让我困扰了小半天,自己摸索的确是比较痛苦,最后在国外一片帖子中看到这么一段描述,有种豁然开朗的感觉。

Inserts, updates, deletes and reads are generally OK from multiple threads, but Brad'sanswer is not correct.  You have to be careful with how you create your connections and use them.  There are situations where your update calls will fail, even if your database doesn't get corrupted.

The basic answer.

The SqliteOpenHelper object holds on to one database connection.  It appears to offer you a read and write connection, but it really doesn't.  Call the read-only, and you'll get the write database connection regardless.

So, one helper instance, one db connection.  Even if you use it from multiple threads, one connection at a time.  The SqliteDatabase object uses java locks to keep access serialized.  So, if 100 threads have one db instance, calls to the actual on-disk database are serialized.

So, one helper, one db connection, which is serialized in java code.  One thread, 1000 threads, if you use one helper instance shared between them, all of your db access code is serial.  And life is good (ish).

If you try to write to the database from actual distinct connections at the same time, one will fail.  It will not wait till the first is done and then write.  It will simply not write your change.  Worse, if you don’t call the right version of insert/update on the SQLiteDatabase, you won’t get an exception.  You’ll just get a message in your LogCat, and that will be it.

So, multiple threads?  Use one helper.  Period.  If you KNOW only one thread will be writing, you MAY be able to use multiple connections, and your reads will be faster, but buyer beware.  I haven't tested that much.

Here's a blog post with far more detail and an example app.

Gray and I are actually wrapping up an ORM tool, based off of his Ormlite, that works natively with Android database implementations, and follows the safe creation/calling structure I describe in the blog post.  That should be out very soon.  Take a look.


In the meantime, there is a follow up blog post:

Also checkout the fork by2point0 of the previously mentioned locking example:



 

关于第二种方式,同样也能解决并发操作SQLite

2.“一个database connect,既有查询又有更新(不同的statement,且不论顺序),执行完之后,不关闭,会产生一个扩展名为s3db-journal的(临时)文件,若再次使用该connect执行更新,则产生“unable to open database file”的异常。 所以,一个connect执行完之后要么close,要么自己处理临时文件。”毕竟不同设备的数据库文件存储路径有所区别,删除可能遇到文件路径错误,文件不存在等问题,所以推荐使用前者,执行完毕,立即关闭close()

@Override
	public synchronized void close() {
		super.close();
	}


 注意:此处和上面案例恰恰相反,每次使用数据都要通过构造函数构造一个SQLHelper实例,如果每次使用同一个SQLHelper,那么关闭后就不能再打开数据库

Cursor cursor = null;
try {
	// helper = SQLHelper.getInstance(context);
	//注意:此处和上面案例恰恰相反,每次使用数据都要通过构造函数构造一个sqlhelper实例
	helper = new SQLHelper(context);
	SQLiteDatabase db = helper.getReadableDatabase();
	cursor = db.query("t_student", null, null, null, null, null, null);
} catch (Exception e) {
	// TODO: handle exception
} finally {
	helper.close(); // 用完立即关闭
}


 

总结:比较了这两种方法,测试过后,强烈推荐使用第一个,从性能的角度,第一种使用单例加同步锁的模式,全局只有一个SQLHelper对象,而第二种方式则是需要多次创建SQLHelper对象,然后关闭,性能远远低于单例的方式

 

 

 


 

 

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SQLite中,数据连接是串行的,这意味着同一时刻只能有一个线程访问数据。然而,SQLite提供了一些机制来支持多线程的操作。 首先,可以在SQLite上设置多线程模式。SQLite提供了三种多线程模式:Serialized、Single-thread和Multi-thread。Serialized模式是默认模式,它将所有连接串行化,不支持多线程操作。Single-thread模式允许多个连接,但只能在同一个线程内共享。Multi-thread模式允许多个连接在多个线程内共享,但要确保每个连接在不同的线程内使用,否则会出现并发问题。 其次,SQLite提供了一个名为sqlite3_threadsafe()的函数,可以用来检查当前SQLite多线程支持情况。该函数返回0表示SQLite不支持多线程操作,返回1表示支持多线程操作。 对于多线程应用程序,我们需要确保每个线程都有自己的数据连接,并且在使用连接前,要确保使用同步机制来避免并发访问数据的问题。常用的同步机制有互斥锁(Mutex)和条件变量(Condition Variable)。互斥锁用于保护对数据访问,只有获取到锁的线程才能执行数据操作;条件变量用于线程之间的通信,可以通过等待条件变量触发来实现线程的同步。 在使用SQLite多线程的过程中,需要注意以下几点: 1. 每个线程都应该有自己的独立数据连接。 2. 在访问数据之前,需要获取互斥锁来保护对数据访问。 3. 避免在多个线程之间共享数据连接对象。 4. 在多线程操作中,要注意处理并发访问的问题,避免出现数据竞争和资源争用。 5. 当不再使用数据连接时,要确保正确关闭连接,释放资源。 总的来说,虽然SQLite是一个轻量级的数据,但是它也提供了一些机制来支持多线程操作。要正确使用SQLite多线程,需要确保每个线程都有自己的数据连接,并且使用同步机制来避免并发访问的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值