没啥诀窍,只需保证几个线程都是用的一个SQLiteDataBase对象就行了。
如果我们非要在不同线程中用两个或更多的SQLiteDataBase对象呢,当然这些SQLiteDataBase对象所操作的是同一个数据库,也就是同一个db文件,这个就是这篇博客的重点了

(ps:使用到的代码将在博文结尾贴出)

第一种情况:一个SQLiteDataBase对象不同线程

两个子线程执行修改数据库操作,三个子线程执行查询数据库对象,

             writethread = new SQLWritethread("write",true,sqLiteUtil);writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);readThread_one = new SQLReadThread("one",true,sqLiteUtil);readThread_two = new SQLReadThread("two",true,sqLiteUtil);readThread_three = new SQLReadThread("three",true,sqLiteUtil);writethread.start();writethread_two.start();readThread_one.start();readThread_two.start();readThread_three.start();
03-17 16:33:56.580 28028-28076/com.example.zth.seven V/zzw: write update
03-17 16:33:56.582 28028-28078/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:33:56.582 28028-28078/com.example.zth.seven V/zzw: one query
03-17 16:33:56.595 28028-28077/com.example.zth.seven V/zzw: write_two update
03-17 16:33:56.596 28028-28079/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:33:56.597 28028-28079/com.example.zth.seven V/zzw: two query
03-17 16:33:56.598 28028-28080/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:33:56.598 28028-28080/com.example.zth.seven V/zzw: three query
03-17 16:33:56.601 28028-28076/com.example.zth.seven V/zzw: write update
03-17 16:33:56.602 28028-28078/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:10 性别:male
03-17 16:33:56.602 28028-28078/com.example.zth.seven V/zzw: one query
03-17 16:33:56.605 28028-28077/com.example.zth.seven V/zzw: write_two update
03-17 16:33:56.606 28028-28079/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:10 性别:male
03-17 16:33:56.607 28028-28079/com.example.zth.seven V/zzw: two query
03-17 16:33:56.609 28028-28080/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:10 性别:male
03-17 16:33:56.609 28028-28080/com.example.zth.seven V/zzw: three query

可以看出同一个SQLiteDataBase对象多线程读写操作没问题

第二种情况,两个SQLiteDataBase对象不同线程

一个子线程执行update函数,所持有的SQLiteDataBase对象是sqLiteUtil,而其他三个子线程执行query函数,所持有的SQLiteDataBase对象是sqLiteUtil_two,

            writethread = new SQLWritethread("write",true,sqLiteUtil);//writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);readThread_one = new SQLReadThread("one",true,sqLiteUtil_two);readThread_two = new SQLReadThread("two",true,sqLiteUtil_two);readThread_three = new SQLReadThread("three",true,sqLiteUtil_two);writethread.start();writethread_two.start();readThread_one.start();readThread_two.start();readThread_three.start();

因为无限循环的时候没有设置sleep,模仿多次读写操作,所以log很多重复的

03-17 16:39:16.634 29175-29295/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:39:16.634 29175-29295/com.example.zth.seven V/zzw: one query
03-17 16:39:16.636 29175-29296/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:39:16.638 29175-29296/com.example.zth.seven V/zzw: two query
03-17 16:39:16.640 29175-29297/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:39:16.640 29175-29297/com.example.zth.seven V/zzw: three query
03-17 16:39:16.642 29175-29295/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
。。。。。。
03-17 16:39:16.650 29175-29296/com.example.zth.seven V/zzw: two query
03-17 16:39:16.665 29175-29294/com.example.zth.seven V/zzw: write update
03-17 16:39:16.668 29175-29294/com.example.zth.seven V/zzw: write update
03-17 16:39:16.975 29175-29294/com.example.zth.seven V/zzw: write update
03-17 16:39:16.977 29175-29294/com.example.zth.seven V/zzw: write update
03-17 16:39:16.980 29175-29294/com.example.zth.seven V/zzw: write update
。。。。。。。。。。
03-17 16:39:17.081 29175-29297/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:1060 性别:male
03-17 16:39:17.081 29175-29297/com.example.zth.seven V/zzw: three query
03-17 16:39:17.083 29175-29295/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:1060 性别:male
03-17 16:39:17.083 29175-29295/com.example.zth.seven V/zzw: one query
03-17 16:39:17.083 29175-29296/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:1060 性别:male
03-17 16:39:17.084 29175-29296/com.example.zth.seven V/zzw: two query
03-17 16:39:17.085 29175-29297/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:1060 性别:male
。。。。。。。。。。。

运行正常

两个个子线程执行update函数,所持有的SQLiteDataBase对象是sqLiteUtil,而其他三个子线程执行query函数,所持有的SQLiteDataBase对象是sqLiteUtil_two,

             writethread = new SQLWritethread("write",true,sqLiteUtil);writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);readThread_one = new SQLReadThread("one",true,sqLiteUtil_two);readThread_two = new SQLReadThread("two",true,sqLiteUtil_two);readThread_three = new SQLReadThread("three",true,sqLiteUtil_two);writethread.start();writethread_two.start();readThread_one.start();readThread_two.start();readThread_three.start();

一开始几秒还好,然后就说子线程执行的query函数有问题

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (Sqlite code 5), (OS error - 2:No such file or directory)

看来在不同线程下读操作不能在多个SQLiteDataBase对象进行写操作的时候来完成

如果是读操作那边有两个SQLiteDataBase对象呢

            writethread = new SQLWritethread("write",true,sqLiteUtil);//writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);readThread_one = new SQLReadThread("one",true,sqLiteUtil);readThread_two = new SQLReadThread("two",true,sqLiteUtil_two);readThread_three = new SQLReadThread("three",true,sqLiteUtil_two);writethread.start();
//            writethread_two.start();readThread_one.start();readThread_two.start();readThread_three.start();

结果一样

android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file (Sqlite code 14), (OS error - 24:Too many open files)

结论:在不同的线程完成读写操作只能使用同一个SQLiteDataBase对象,或者写操作使用一个SQLiteDataBase对象,读操作使用一个SQLiteDataBase对象

代码:

public class SQLiteActivity extends Activity {/** Called when the activity is first created. */
//声明各个按钮private Button createBtn;private Button insertBtn;private Button updateBtn;private Button queryBtn;private Button deleteBtn;private Button ModifyBtn,btn_thread_start,btn_thread_stop;private SQLiteUtil sqLiteUtil,sqLiteUtil_two;private SQLWritethread writethread,writethread_two;private SQLReadThread readThread_one;private SQLReadThread readThread_two;private SQLReadThread readThread_three;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_five);//调用creatView方法creatView();
//setListener方法setListener();}//通过findViewById获得Button对象的方法private void creatView(){createBtn = (Button)findViewById(R.id.createDatabase);updateBtn = (Button)findViewById(R.id.updateDatabase);insertBtn = (Button)findViewById(R.id.insert);ModifyBtn = (Button)findViewById(R.id.update);queryBtn = (Button)findViewById(R.id.query);deleteBtn = (Button)findViewById(R.id.delete);btn_thread_start = (Button)findViewById(R.id.btn_thread_start);btn_thread_stop = (Button)findViewById(R.id.btn_thread_stop);}//为按钮注册监听的方法private void setListener(){createBtn.setOnClickListener(new CreateListener());updateBtn.setOnClickListener(new UpdateListener());insertBtn.setOnClickListener(new InsertListener());ModifyBtn.setOnClickListener(new ModifyListener());queryBtn.setOnClickListener(new QueryListener());deleteBtn.setOnClickListener(new DeleteListener());btn_thread_start.setOnClickListener(new ThreadStartListener());btn_thread_stop.setOnClickListener(new ThreadStopListener());sqLiteUtil = new SQLiteUtil(this);sqLiteUtil_two = new SQLiteUtil(this);}//创建数据库的方法class CreateListener implements OnClickListener{@Overridepublic void onClick(View v) {sqLiteUtil.initSQL();}}//升级数据库的方法class UpdateListener implements OnClickListener{@Overridepublic void onClick(View v) {sqLiteUtil.upgradeSQL();}}//插入数据的方法class InsertListener implements OnClickListener{@Overridepublic void onClick(View v) {sqLiteUtil.insertSQL();}}//查询数据的方法class QueryListener implements OnClickListener{@Overridepublic void onClick(View v) {sqLiteUtil.querySQL();}}//修改数据的方法class ModifyListener implements OnClickListener{@Overridepublic void onClick(View v) {sqLiteUtil.updateSQL();}}//删除数据的方法class DeleteListener implements OnClickListener{@Overridepublic void onClick(View v) {sqLiteUtil.deleteSQL();}}class ThreadStartListener implements OnClickListener{@Overridepublic void onClick(View v) {writethread = new SQLWritethread("write",true,sqLiteUtil);//writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);readThread_one = new SQLReadThread("one",true,sqLiteUtil);readThread_two = new SQLReadThread("two",true,sqLiteUtil_two);readThread_three = new SQLReadThread("three",true,sqLiteUtil_two);writethread.start();
//            writethread_two.start();readThread_one.start();readThread_two.start();readThread_three.start();}}class ThreadStopListener implements OnClickListener{@Overridepublic void onClick(View v) {writethread.setState(false);//     writethread_two.setState(false);readThread_one.setState(false);readThread_two.setState(false);readThread_three.setState(false);}}@Overrideprotected void onDestroy() {writethread.setState(false);//   writethread_two.setState(false);readThread_one.setState(false);readThread_two.setState(false);readThread_three.setState(false);sqLiteUtil_two.closeSQL();sqLiteUtil.closeSQL();super.onDestroy();}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent"><TextViewandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:text="hello"/><Buttonandroid:id="@+id/createDatabase"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="创建数据库"/><Buttonandroid:id="@+id/updateDatabase"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="更新数据库"/><Buttonandroid:id="@+id/insert"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="插入数据"/><Buttonandroid:id="@+id/update"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="更新数据"/><Buttonandroid:id="@+id/query"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="查询数据"/><Buttonandroid:id="@+id/delete"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="删除数据"/><Buttonandroid:id="@+id/btn_thread_start"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="开启多线程"/><Buttonandroid:id="@+id/btn_thread_stop"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="停止多线程"/>
</LinearLayout>
public class SQLiteUtil {private Context context;private SQLiteDatabase db;public SQLiteUtil(Context context){this.context = context;StuDBHelper dbHelper = new StuDBHelper(context,"stu_db",null,1);
//得到一个可写的数据库db =dbHelper.getReadableDatabase();}public void closeSQL(){if(db != null&& db.isOpen()){db.close();}}public void initSQL(){//创建StuDBHelper对象StuDBHelper dbHelper = new StuDBHelper(context,"stu_db",null,1);
//得到一个可读的SQLiteDatabase对象SQLiteDatabase db =dbHelper.getReadableDatabase();}public void upgradeSQL(){
// 数据库版本的更新,由原来的1变为2StuDBHelper dbHelper = new StuDBHelper(context,"stu_db",null,2);SQLiteDatabase db =dbHelper.getReadableDatabase();}public void deleteSQL(){db.beginTransaction();  //手动设置开始事务try{//批量处理操作String whereClauses = "id=?";String [] whereArgs = {String.valueOf(1)};
//调用delete方法,删除数据db.delete("stu_table", whereClauses, whereArgs);db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交}catch(Exception e){}finally{db.endTransaction(); //处理完成}}public void insertSQL(){db.beginTransaction();  //手动设置开始事务try{//批量处理操作
//生成ContentValues对象 //key:列名,value:想插入的值ContentValues cv = new ContentValues();
//往ContentValues对象存放数据,键-值对模式cv.put("id", 1);cv.put("sname", "xiaoming");cv.put("sage", 21);cv.put("ssex", "male");
//调用insert方法,将数据插入数据库db.insert("stu_table", null, cv);db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交}catch(Exception e){}finally{db.endTransaction(); //处理完成}}public void updateSQL(){db.beginTransaction();  //手动设置开始事务try{//批量处理操作ContentValues cv = new ContentValues();cv.put("sage", "23");
//where 子句 "?"是占位符号,对应后面的"1",String whereClause="id=?";String [] whereArgs = {String.valueOf(1)};
//参数1 是要更新的表名
//参数2 是一个ContentValeus对象
//参数3 是where子句db.update("stu_table", cv, whereClause, whereArgs);db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交}catch(Exception e){}finally{db.endTransaction(); //处理完成}}public void updateSQL(int sum){db.beginTransaction();  //手动设置开始事务try{//批量处理操作ContentValues cv = new ContentValues();cv.put("sage", ""+sum);
//where 子句 "?"是占位符号,对应后面的"1",String whereClause="id=?";String [] whereArgs = {String.valueOf(1)};
//参数1 是要更新的表名
//参数2 是一个ContentValeus对象
//参数3 是where子句db.update("stu_table", cv, whereClause, whereArgs);db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交}catch(Exception e){}finally{db.endTransaction(); //处理完成}}public void querySQL(){
//参数1:表名
//参数2:要想显示的列
//参数3:where子句
//参数4:where子句对应的条件值
//参数5:分组方式
//参数6:having条件
//参数7:排序方式db.beginTransaction();  //手动设置开始事务try{//批量处理操作Cursor cursor = db.query("stu_table", new String[]{"id","sname","sage","ssex"}, "id=?", new String[]{"1"}, null, null, null);while(cursor.moveToNext()){String name = cursor.getString(cursor.getColumnIndex("sname"));String age = cursor.getString(cursor.getColumnIndex("sage"));String sex = cursor.getString(cursor.getColumnIndex("ssex"));Log.v("zzw","query------->" + "姓名:"+name+" "+"年龄:"+age+" "+"性别:"+sex);}db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交}catch(Exception e){}finally{db.endTransaction(); //处理完成}}
}
public class SQLReadThread extends Thread {private boolean state = true;private SQLiteUtil sqLiteUtil;public void setState(boolean state){this.state = state;}public SQLReadThread(String name,boolean state,SQLiteUtil sqLiteUtil) {super(name);this.state = state;this.sqLiteUtil = sqLiteUtil;}@Overridepublic void run() {for (int i = 0;state; i++) {sqLiteUtil.querySQL();Log.v("zzw",getName()+" query");}}
}
public class SQLWritethread extends Thread {private boolean state = true;private SQLiteUtil sqLiteUtil;public void setState(boolean state){this.state = state;}public SQLWritethread(String name,boolean state,SQLiteUtil sqLiteUtil) {super(name);this.state = state;this.sqLiteUtil = sqLiteUtil;}@Overridepublic void run() {for (int i = 0;state; i++) {if(getName().equals("write_two"))sqLiteUtil.updateSQL(i*10);elsesqLiteUtil.updateSQL(i*10);Log.v("zzw",getName()+" update");}}
}
public class StuDBHelper extends SQLiteOpenHelper {private static final String TAG = "TestSQLite";public static final int VERSION = 1;//必须要有构造函数public StuDBHelper(Context context, String name, CursorFactory factory,int version) {super(context, name, factory, version);}// 当第一次创建数据库的时候,调用该方法@Overridepublic void onCreate(SQLiteDatabase db) {String sql = "create table stu_table(id int,sname varchar(20),sage int,ssex varchar(10))";
//输出创建数据库的日志信息Log.i(TAG, "create Database------------->");
//execSQL函数用于执行SQL语句db.execSQL(sql);}//当更新数据库的时候执行该方法public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//输出更新数据库的日志信息Log.i(TAG, "update Database------------->");}
}

题外话:你是不是觉得我应该使用getWriteableDataBase,但我可以负责的告诉你基本两种函数得到的SQLiteDatabase对象都能进行读写操作,只会在存储空间不够的情况下才有点区别。(ps:我的64G大存储可能吗)

SQLite线程同步源码分析

加点东西,看了点源码分析了一下SQLite如何完成线程同步,我的切入点就是SQLite如何自己实现的线程同步
我们首先分析读操作也就是query命令如何实现线程同步,跟着我command加鼠标左键看源码

首先query方法里又放了一个query,很正常,加默认参数,(ps:源码在SQLiteDatabase类)

    public Cursor query(String table, String[] columns, String selection,String[] selectionArgs, String groupBy, String having,String orderBy) {return query(false, table, columns, selection, selectionArgs, groupBy,having, orderBy, null /* limit */);}

还是加默认参数,(ps:源码在SQLiteDatabase类)

    public Cursor query(boolean distinct, String table, String[] columns,String selection, String[] selectionArgs, String groupBy,String having, String orderBy, String limit) {return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,groupBy, having, orderBy, limit, null);}

到了真正实现query方法的源码了(ps:源码在SQLiteDatabase类)

    public Cursor queryWithFactory(CursorFactory cursorFactory,boolean distinct, String table, String[] columns,String selection, String[] selectionArgs, String groupBy,String having, String orderBy, String limit, CancellationSignal cancellationSignal) {acquireReference();try {String sql = SQLiteQueryBuilder.buildQueryString(distinct, table, columns, selection, groupBy, having, orderBy, limit);return rawQueryWithFactory(cursorFactory, sql, selectionArgs,findEditTable(table), cancellationSignal);} finally {releaseReference();}}

看到了acquireReference()和releaseReference(),不是感觉很可疑,不要急,我们再看看rawQueryWithFactory方法的源码(ps:源码在SQLiteDatabase类)

    public Cursor rawQueryWithFactory(CursorFactory cursorFactory, String sql, String[] selectionArgs,String editTable, CancellationSignal cancellationSignal) {acquireReference();try {SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,cancellationSignal);return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,selectionArgs);} finally {releaseReference();}}

又看到了这个两个函数,但是我们还要看看driver.query源码(ps:源码在SQLiteCursorDriver类)

    Cursor query(CursorFactory factory, String[] bindArgs);

没了,只是个接口函数,好了我们再来看看acquireReference()和releaseReference()的源码,(ps:源码在SQLiteClosable类)

    public void acquireReference() {synchronized(this) {if (mReferenceCount <= 0) {throw new IllegalStateException("attempt to re-open an already-closed object: " + this);}mReferenceCount++;}}public void releaseReference() {boolean refCountIsZero = false;synchronized(this) {refCountIsZero = --mReferenceCount == 0;}if (refCountIsZero) {onAllReferencesReleased();}}

感觉是通过Synchronized同步锁锁住自己,来完成线程同步,因为SQLiteDatabase是这个抽象类SQLiteClosable的子类,还不能做判断,再看看onAllReferencesReleased()源码

protected abstract void onAllReferencesReleased();

抽象函数,。。。。。。没关系,看看SQLiteDatabase如何实现这个抽象函数

    @Overrideprotected void onAllReferencesReleased() {dispose(false);}private void dispose(boolean finalized) {final SQLiteConnectionPool pool;synchronized (mLock) {if (mCloseGuardLocked != null) {if (finalized) {mCloseGuardLocked.warnIfOpen();}mCloseGuardLocked.close();}pool = mConnectionPoolLocked;mConnectionPoolLocked = null;}if (!finalized) {synchronized (sActiveDatabases) {sActiveDatabases.remove(this);}if (pool != null) {pool.close();}}}

又是同步锁,来防止同时执行多次close(),源码就看到这里,我们再看看官方说明,(PS:百度翻译的)

acquireReference()
获取对对象的引用。

close()
释放一个对对象的引用,如果最后一个引用被释放,则关闭对象。

releaseReference()
释放一个对对象的引用,如果最后一个引用被释放,则关闭对象。

但是这里我们注意一下mReferenceCount的默认值为1

private int mReferenceCount = 1;

acquireReference()和releaseReference()时是成双成对的出现,一个加,一个减,除非我们调用一次close,否则他不会关闭对象的

结论:SQLiteDatabase通过同步锁锁自己的对象来完成在多线程下读写线程同步,并且通过控制mReferenceCount来中断读写操作,因为锁住的是对象所以对于不同对象的SQLiteDatabase对同一个数据库文件的过频繁或同时的打开就会导致错误

Android SQLite多线程读写和线程同步源码分析相关推荐

  1. 线程池源码分析-FutureTask

    1 系列目录 线程池接口分析以及FutureTask设计实现 线程池源码分析-ThreadPoolExecutor 该系列打算从一个最简单的Executor执行器开始一步一步扩展到ThreadPool ...

  2. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  3. Android 9 (P)之init进程启动源码分析指南之三

          Android 9 (P)之init进程启动源码分析指南之三 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 An ...

  4. Tomcat集群应用同步 —— 源码分析

    文章目录 前言 一.应用同步的配置与实现原理 二.应用同步源码分析 三.如何获取集群的节点列表 四.通讯模块Tribe 五.集群的Session同步 六.集群的Session共享 总结 前言 相信大家 ...

  5. Android 8.1/9.0 MTK Camera源码分析之录像快门声音控制流程

    前面已经针对拍照快门声音控制流程进行了分析,接下来分析一下录像快门声音的控制流程. Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程 这两篇文章其实都是相对于手机系统RO ...

  6. Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程

    Android 8.1/9.0 MTK Camera源码分析之快门声音控制 在Android 8.1上mtk camera有控制快门声音的接口,但是并没有了控制录像快门声音的接口.之所以会有这个现象, ...

  7. 吐血整理:Java线程池源码分析(基于JDK1.8建议收藏)

    文章目录 一.引言 二.线程池的参数介绍 1.ThreadPoolExecutor的UML图 三.线程池的使用 1.线程池的工作原理 2.线程池类型 2.1.newCachedThreadPool使用 ...

  8. 解密android日志xlog,XLog 详解及源码分析

    一.前言 这里的 XLog 不是微信 Mars 里面的 xLog,而是elvishew的xLog.感兴趣的同学可以看看作者 elvishwe 的官文史上最强的 Android 日志库 XLog.这里先 ...

  9. 【C++】Android (Light)RefBase-sp-wp引用计数-智能指针源码分析

    文章目录 1.RefBase简介 2.RefBase源码分析 3.RefBase使用注意事项 4.总结 1.RefBase简介 什么是RefBase?RefBase是Android中的一个C++类,用 ...

最新文章

  1. Android之Log工具类使用
  2. KAIXIN000发狠 誓将匿名制进行到底!
  3. 网络流媒体协议 RTSP协议
  4. 给我一个兴趣点,我就能撬动一个行业
  5. open ai gpt_让我们来谈谈将GPT-3 AI推文震撼到核心的那条推文
  6. Unhandled event loop exception PermGen space
  7. 牛客题霸 [栈和排序] C++题解/答案
  8. 微信支付官方SDK V3 .NET版的坑
  9. u盘解密软件_企业都使用哪些数据防泄密软件
  10. 5555555,老粘不上来。。
  11. C语言 “fread” 和 “fwrite”的简单介绍
  12. 身份证号码验证处理工具类
  13. vue 二级三级路由配置
  14. ebay增强可用性的4个原则
  15. apidoc生成文档时报错
  16. 美国东部时间和北京时间之间的转换
  17. 什么软件可以代替sc防火墙_车玻璃水的成份是什么?普通肥皂水和清水可以代替吗?...
  18. webgl_图形变换(旋转,平移,缩放)
  19. 《50个教育法:我把三个儿子送入了斯坦福》书中的精髓:了解教育的本质,以言传身教、耐心引导的教育方式培养孩子成才。
  20. Vue开发项目入门——Vue脚手架

热门文章

  1. 微信H5棋牌游戏APP下载链接被屏蔽的解决办法
  2. 基于Python实现的费诺编码
  3. 用火狐浏览器看中一段代码是复制外部html还是复制内部html,为什么PDF文件用某些浏览器读取就可以复制出其中的文字,而用WPS等办公软体就不可以复制出呢?...
  4. 解决每次运行终端,自动激活conda环境问题
  5. PCB设计指南——关于过孔
  6. VisualStudio2019,基于.NET Framework的单元测试
  7. jvm原理(25)Java字节码文件结构剖析
  8. 功率单位dBm及其换算
  9. ArcGIS立体效果地图制作
  10. 线上科普vr展厅设计供应 广交会布展