今天在看博客的時候突然遇到了同步代碼塊——Synchronized,猛然間想到自己這次面試的時候面試官特意問了關於java線程同步鎖的問題,當時自己也是知道那麼一點單例模式,就主要聊了下單例模式,而同步代碼塊卻比較陌生,所以就 特地圍繞這點代碼塊折騰起來。
下面是這次的過程和結果,大家一起來感悟一下;
/**
* 同步線程
*/
class SyncThread implements Runnable {
private static int count;
private final String TAG="SyncThread";
public SyncThread() {
count = 0;
Log.d(TAG,"我執行了,count="+count);
}
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
try {
Log.d(TAG,Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
接下來我們就來看這段代碼的調用:
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "JACK");
Thread thread2 = new Thread(syncThread, "ROSE");
thread1.start();
thread2.start();
首先來簡單分析一下這段代碼:這是一個實現了Runnable接口的類,也就是一個線程類,在這個類中有三點引起了我的注意:
第一:靜態變量count , 屬於全局變量,當頁面銷燬的時候並沒有被銷燬,這裏提一下,要注意預防內存泄漏;
第二:在這個類的構造方法中初始化了count;
第三:在線程的run方法裏面加了一把同步鎖;
第四:thread1和thread2共用一個SyncThread實例。
下面的一系列動作就圍繞着這四個特點展開了。
第一次試驗:上面的代碼不做任何更改的直接運行看看運行結果:
由運行結果可以知道,JACK線程在執行的時候,synchronized會被加鎖,導致run方法就被阻塞,ROSE線程只有在JACK線程執行完成之後纔會執行;
第二次試驗:把thread1和thread2的SyncThread分別new出來,修改調用代碼如下:
SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "JACK");
Thread thread2 = new Thread(syncThread2, "ROSE");
thread1.start();
thread2.start();
運行效果如下:
首先我們不難看出,SyncThread構造方法調用了兩次,而且是連續的兩次,這個也還好理解,因爲連續構建了兩次SyncThread的對象,而後面的邏輯貌似就不正常了,明明有用到同步鎖啊,怎麼會沒有作用???
這個問題我們先留着,在第二次試驗的基礎上,進行第三次試驗,我們把同步鎖去掉看看效果,修改Runnable實現類的run方法如下:
public void run() {
for (int i = 0; i < 5; i++) {
try {
Log.d(TAG,Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
接下來我們來看看運行結果:
現在只有一個地方不一樣,就是加鎖是rose然後jack,不加鎖是jack然後rose,爲了驗證這個問題是不是偶然,把代碼恢復成第二次試驗的時候,連續運行了三次都是下面的輸出:
這裏就可以證明rose在前jack在後是偶然的,畢竟兩個線程是異步執行(沒有先後順序),中間的兩行長的先忽略,貌似跟本題無關,(若有關還請高人指點。。。);
那麼就剩下最後一個問題,線程加鎖在非單例模式下的運行流程是什麼???