Android中多线程同步问题

本文探讨了多线程环境下数据库操作可能导致的数据安全问题,并通过一个具体的Android应用案例,展示了如何使用synchronized关键字来实现线程间的同步,避免数据冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

多线程

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

/**
 * <pre>
 *     author : ada
 *     time   : 2017/03/30
 *     desc   :
 *     version: 1.0
 * </pre>
 */
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;

/**
 * <pre>
 *     author : ada
 *     time   : 2017/03/30
 *     desc   :
 *     version: 1.0
 * </pre>
 */
public class CountDao {
    private static DatabaseHelper helper;

    public CountDao(Context context) {
        helper = new DatabaseHelper(context);

    }

    public static void addCountList(List<PersonModel> 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<PersonModel> list) {
...
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值