Android中多线程同步问题

在最近的项目中有用到数据库这块儿,遇到了一些线程同步的问题,通过查资料希望弄懂这个问题.

多线程

多线程在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();

程序抛异常了,日志如下:
Android中多线程同步问题_第1张图片

可以看到线程id为996的线程虽然后开始操作数据库,但是却先完成,这是因为list的数量为30,而list1的数量为100.耗时不同.
两个线程同时操作数据库,996的线程操作完后,把数据库关了,而这时995的线程还没插入完成,当其再次操作数据库时,就会抛出的java.lang.IllegalStateException异常.

那么怎么解决这个问题呢?
解决方法就是利用synchronized关键字,给方法加锁,使用同步方法操作数据库.

public static synchronized void addCountList(List list) {
...
}

这样当一个线程操作数据库时,另一个线程就会处于等待状态,等待前面的线程操作完成后,后面的线程才能操作数据库.

你可能感兴趣的:(Android)