鎖
比如有三個線程thread-0, thread-1, thread-3
三個線程同時在執行一個帶鎖的方法時,如果thread-0先獲得了鎖,那麼其它二個線程就只能外面等着,直到thread-0把鎖給釋放了,這個時候就由其它線程去爭取這個鎖.
生活中的例子: 現在有三個人同時都要上廁所A,B,C . A先進入到了廁所,這個時候A就會把門給鎖了,B,C就要在外面等,直到A上完出來把門打開,這個時候B,C纔能有一個人進去,進去後也是鎖門,出來也是解鎖,然後繼續下一個人.
鎖可以解決多線程中線程安全的問題.
在android中的單例(懶漢式)
public class SingleTon {
private static SingleTon sInstance;
public static SingleTon getInstance() {
if (sInstance ==null) {
//1:
sInstance = new SingleTon();
}
return sInstance;
}
}
如果多線程調用這個getInstance的話就會有問題,下面分析一下
同樣有三個線程: thread-0, thread-1, thread-2
註釋1處: 假如thread-0執行到1處時候cpu執行器被操作系統調度給釋放了,這時thread-1進來了,發現sInstance是空的,就會執行下new代碼,最後執行完了,這個時候thread-0恢復了獲得了cpu執行權,thread-0也new出了一個,這樣這個sInstance被創建了二次,就會引發安全性問題
解決方法,加synchronized鎖住,被稱之爲隱式鎖,因爲加鎖解鎖我們都看不到,是內部自己完成的.
public class SingleTon {
private static SingleTon sInstance;
public synchronized static SingleTon getInstance() {
if (sInstance ==null) {
sInstance = new SingleTon();
}
return sInstance;
}
}
synchronized使用方法
作用在靜態方法之上執行的是一個類鎖xxx.class
作用在非靜態方法之上執行的是當前的對象鎖Object object;
上面那個寫法會有性能問題,因爲每個線程都會在方法外等待,所以性能不是非常好,改進版本
public class SingleTon {
private static SingleTon sInstance;
public static SingleTon getInstance() {
if (sInstance == null) {
synchronized (SingleTon.class) {
if (sInstance == null) {
sInstance = new SingleTon();
}
}
}
return sInstance;
}
}
這二種方法執行的鎖其實是同一個鎖
這裏再多提一嘴靜態方法的synchronized和非靜態方法的區別
public class SingleTon {
private static SingleTon sInstance;
public synchronized static SingleTon getInstance() {
if (sInstance ==null) {
sInstance = new SingleTon();
}
return sInstance;
}
}
public class SingleTon {
private static SingleTon sInstance;
public static SingleTon getInstance() {
synchronized(SingleTon.class){
if (sInstance ==null) {
sInstance = new SingleTon();
}
}
return sInstance;
}
}
上面二個方法是等價的
public class SingleTon {
private static SingleTon sInstance;
public synchronized SingleTon getInstance() {
if (sInstance ==null) {
sInstance = new SingleTon();
}
return sInstance;
}
}
public class SingleTon {
private static SingleTon sInstance;
public SingleTon getInstance() {
synchronized(this){
if (sInstance ==null) {
sInstance = new SingleTon();
}
}
return sInstance;
}
}
這上面二個方法也是等價的
顯示鎖 Lock: 我們程序員可以自己控制
在下面這個demo中可以發現在二個線程都執行add這個方法的時候會出現線程安全問題,解決方法有二種
1:在方法上加個synchronized,第二種方法使用Lock這個來操作加鎖解鎖, synchronized天生是可重入鎖
public class LockDemo {
private int mCount = 0;
private Lock mLock = new ReentrantLock();//這是一個可重入鎖
public void add() {//有問題
mCount++;
}
public void add() {//沒問題
mLock.lock();//自己加鎖
mCount++;//如果這裏發生異常,下面就不會解鎖,就會導致所有線程都進不來
mLock.unlock();//自己解鎖
}
public void add() {//沒問題
try {
mLock.lock();
mCount++;
}finally {
mLock.unlock();
}
}
public synchronized void add() {//沒問題
mCount++;
}
private static class MyThread extends Thread {
LockDemo mLockDemo;
public MyThread(LockDemo lockDemo) {
mLockDemo = lockDemo;
}
@Override
public void run() {
super.run();
for (int i = 0; i < 10000; i++) {
mLockDemo.add();
}
}
}
public static void main(String[] args) {
LockDemo lockDemo = new LockDemo();
new MyThread(lockDemo).start();
new MyThread(lockDemo).start();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count: " + lockDemo.mCount);
}
}