在最近的项目中有用到数据库这块儿,遇到了一些线程同步的问题,通过查资料希望弄懂这个问题.
多线程在java和android中都有用到,java中主要是为了提高CPU的利用效率,Android主要是为了防止产生ANR异常.
1>提高效率的方法,多线程===>>>并发
2>ANR===>>>Android的主线程做耗时操作会产生ANR,因此把耗时的操作放在子线程中
1>线程安全
2>性能开销
下面用一个例子演示多线程带来的数据安全问题:
主页面,布局不贴了,就一个Button
MainActivity
package com.example.study0404;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
CountDao countDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
countDao = new CountDao(MainActivity.this);
Button bt_add = (Button) findViewById(R.id.bt_add);
final List list1 = new ArrayList<>();
for (int i = 0; i < 100; i++) {
PersonModel model = new PersonModel();
model.setName("name-1-" + i);
model.setAge(1);
list1.add(model);
}
final List list2 = new ArrayList<>();
for (int i = 0; i < 30; i++) {
PersonModel model = new PersonModel();
model.setName("name-2-" + i);
model.setAge(2);
list2.add(model);
}
bt_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
public void run() {
CountDao.addCountList(list1);
};
}.start();
new Thread() {
public void run() {
CountDao.addCountList(list2);
};
}.start();
}
});
}
}
然后是数据库创建类:
DatabaseHelper
package com.example.study0404;
import java.io.File;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
/**
*
* author : ada
* time : 2017/03/30
* desc :
* version: 1.0
*
*/
public class DatabaseHelper extends SQLiteOpenHelper {
private final String sql = "CREATE TABLE IF NOT EXISTS person (personid integer primary key autoincrement, name varchar(20), age INTEGER)";
public DatabaseHelper(Context context) {
// super(context, Constant.DB_NAME, null, Constant.DB_VERSION);
super(context, getMyDatabaseName(Constant.DB_NAME), null,
Constant.DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
private static String getMyDatabaseName(String name) {
String databasename = name;
boolean isSdcardEnable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {// SDCard是否插入
isSdcardEnable = true;
}
String dbPath = null;
if (isSdcardEnable) {
dbPath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/study/database/";
} else {// 未插入SDCard,建在内存中
}
File dbp = new File(dbPath);
if (!dbp.exists()) {
dbp.mkdirs();
}
databasename = dbPath + databasename;
return databasename;
}
}
然后是数据库业务类,很简单,就一个批量插入数据库的操作
CountDao
package com.example.study0404;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import java.util.List;
/**
*
* author : ada
* time : 2017/03/30
* desc :
* version: 1.0
*
*/
public class CountDao {
private static DatabaseHelper helper;
public CountDao(Context context) {
helper = new DatabaseHelper(context);
}
public static void addCountList(List list) {
if (!(list != null && list.size() > 0)) {
return ;
}
String name = Thread.currentThread().getName();
Log.e("main","<<===start===>>>" + name);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
for (PersonModel bean : list) {
values.put("name", bean.getName());
values.put("age", bean.getAge());
long insert = db.insert("person", null, values);
}
db.close();
String success_name = Thread.currentThread().getName();
Log.e("main","<<===success===>>>" + success_name);
}
}
然后把程序跑起来,点击Button,开启两个线程同时操作数据库:
new Thread() {
public void run() {
CountDao.addCountList(list1);
};
}.start();
new Thread() {
public void run() {
CountDao.addCountList(list2);
};
}.start();
可以看到线程id为996的线程虽然后开始操作数据库,但是却先完成,这是因为list的数量为30,而list1的数量为100.耗时不同.
两个线程同时操作数据库,996的线程操作完后,把数据库关了,而这时995的线程还没插入完成,当其再次操作数据库时,就会抛出的java.lang.IllegalStateException异常.
那么怎么解决这个问题呢?
解决方法就是利用synchronized关键字,给方法加锁,使用同步方法操作数据库.
public static synchronized void addCountList(List list) {
...
}
这样当一个线程操作数据库时,另一个线程就会处于等待状态,等待前面的线程操作完成后,后面的线程才能操作数据库.