这是一个及其诡异的死锁,很少有人意识到,在Androind中,SQLiteDatabase.beginTransaction的实现里,也要等到SQLiteConnectionPool中此db的primaryConnect可用才行。也就是说,beginTransaction和endTransaction也在某种程序上等同于锁定和解锁,在和synchronized(sLocker)嵌套使用时,同样要考虑死锁的问题。
一般来讲,如果看到SQLiteConnectionPool的很长的超时警告,就要考虑死锁的可能性。下面是死锁的模拟:
- package com.mat.testdeadlock;
- import android.database.sqlite.SQLiteDatabase;
- import android.os.Bundle;
- import android.support.v7.app.AppCompatActivity;
- import android.util.Log;
- public class MainActivity extends AppCompatActivity {
- static Object sLocker = new Object();
- MySqlLiteHlelper myHelper;
- Runnable run1 = new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(3000);
- SQLiteDatabase db = myHelper.getWritableDatabase();
- Log.d(”DEADLOCK”, “Before beginTransaction”);
- db.beginTransaction();
- Log.d(”DEADLOCK”, “After beginTransaction”);
- Thread.sleep(3000);
- Log.d(”DEADLOCK”, “Before synchronized”);
- synchronized (sLocker)
- {
- Log.d(”DEADLOCK”, “In synchronized”);
- }
- try{
- db.setTransactionSuccessful();
- }finally {
- db.endTransaction();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- Runnable run2 = new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(3000);
- Log.d(”DEADLOCK”, “Before synchronized”);
- synchronized (sLocker)
- {
- Log.d(”DEADLOCK”, “In synchronized”);
- Thread.sleep(3000);
- SQLiteDatabase db = myHelper.getWritableDatabase();
- Log.d(”DEADLOCK”, “Before beginTransaction”);
- db.beginTransaction();
- Log.d(”DEADLOCK”, “After beginTransaction”);
- try{
- db.setTransactionSuccessful();
- }finally {
- db.endTransaction();
- } }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- myHelper = new MySqlLiteHlelper(this);
- }
- @Override
- protected void onStart() {
- super.onStart();
- new Thread(run1,“Thread-1”).start();
- new Thread(run2,“Thread-2”).start();
- }
- }
package com.mat.testdeadlock;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
static Object sLocker = new Object();
MySqlLiteHlelper myHelper;
Runnable run1 = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
SQLiteDatabase db = myHelper.getWritableDatabase();
Log.d("DEADLOCK", "Before beginTransaction");
db.beginTransaction();
Log.d("DEADLOCK", "After beginTransaction");
Thread.sleep(3000);
Log.d("DEADLOCK", "Before synchronized");
synchronized (sLocker)
{
Log.d("DEADLOCK", "In synchronized");
}
try{
db.setTransactionSuccessful();
}finally {
db.endTransaction();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
Runnable run2 = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
Log.d("DEADLOCK", "Before synchronized");
synchronized (sLocker)
{
Log.d("DEADLOCK", "In synchronized");
Thread.sleep(3000);
SQLiteDatabase db = myHelper.getWritableDatabase();
Log.d("DEADLOCK", "Before beginTransaction");
db.beginTransaction();
Log.d("DEADLOCK", "After beginTransaction");
try{
db.setTransactionSuccessful();
}finally {
db.endTransaction();
} }
} catch (Exception e) {
e.printStackTrace();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myHelper = new MySqlLiteHlelper(this);
}
@Override
protected void onStart() {
super.onStart();
new Thread(run1,"Thread-1").start();
new Thread(run2,"Thread-2").start();
}
}
结果:
04-22 12:28:38.473 972-1006/com.mat.testdeadlock D/DEADLOCK: Before synchronized
04-22 12:28:38.473 972-1006/com.mat.testdeadlock D/DEADLOCK: In synchronized
04-22 12:28:38.551 972-1005/com.mat.testdeadlock D/DEADLOCK: Before beginTransaction
04-22 12:28:38.551 972-1005/com.mat.testdeadlock D/DEADLOCK: After beginTransaction
04-22 12:28:41.474 972-1006/com.mat.testdeadlock D/DEADLOCK: Before beginTransaction —此时,1005 hold db, 1006 waiting sLocker
04-22 12:28:41.552 972-1005/com.mat.testdeadlock D/DEADLOCK: Before synchronized –此时,1006 hold sLocker, 1005 waiting db
04-22 12:29:11.474 972-1006/com.mat.testdeadlock W/SQLiteConnectionPool: The connection pool for database ‘/data/user/0/com.mat.testdeadlock/databases/my.db’ has been unable to grant a connection to thread 2462 (Thread-2) with flags 0x2 for 30.000002 seconds. Connections: 0 active, 1 idle, 0 available
.
.
.
04-22 12:41:41.488 972-1006/com.mat.testdeadlock W/SQLiteConnectionPool: The connection pool for database ‘/data/user/0/com.mat.testdeadlock/databases/my.db’ has been unable to grant a connection to thread 2462 (Thread-2) with flags 0x2 for780.01404 seconds. Connections: 0 active, 1 idle, 0 available.
</div>