【185期】面試官:你能說說 Synchronized實現(xiàn)對象鎖的兩種方式以及它的原理嗎?
閱讀本文大概需要 4.5 分鐘。
來自:blog.csdn.net/x541211190/article/details/106179245
一.同步代碼塊鎖
count,各自執(zhí)行1萬次count++,驗證結(jié)果是否等于2萬,而不會出現(xiàn)小于2萬的情況。public class SynchronizeCodeBlockLock implements Runnable {
private static SynchronizeCodeBlockLock instance = new SynchronizeCodeBlockLock();
private static int count = 0;
@Override
public void run() {
method();
}
private void method() {
// 關(guān)鍵:同步代碼塊的方式,操作同一變量,達到線程安全的效果
synchronized (this) {
System.out.println("線程名:" + Thread.currentThread().getName() + ",運行開始");
for (int i = 0; i < 10000; i++) {
count++;
}
System.out.println("線程:" + Thread.currentThread().getName() + ",運行結(jié)束");
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
// 若有線程如果還在活動,則不執(zhí)行下一步(等同于thread.join()方法)
}
System.out.println("期待結(jié)果:20000,實際結(jié)果:" + count);
}
}
線程名:Thread-0,運行開始
線程:Thread-0,運行結(jié)束
線程名:Thread-1,運行開始
線程:Thread-1,運行結(jié)束
期待結(jié)果:20000,實際結(jié)果:20000
synchronized關(guān)鍵字后,線程Thread-0先執(zhí)行,等到其結(jié)束后,Thread-1才開始執(zhí)行。如果不使用synchronized關(guān)鍵字,執(zhí)行結(jié)果可能會是Thread-0和Thread-1幾乎同時執(zhí)行,幾乎同時運行結(jié)束。這就說明使用了synchronized關(guān)鍵字后,將多個線程的并行,變?yōu)榱舜小?/section>二.方法鎖
public class MethodLock implements Runnable {
private static MethodLock instance = new MethodLock();
@Override
public void run() {
method();
}
//關(guān)鍵:synchronized可以保證此方法被順序執(zhí)行,線程1執(zhí)行完4秒鐘后,線程2再執(zhí)行4秒。不加synchronized,線程1和線程2將同時執(zhí)行
private synchronized void method() {
System.out.println("線程:" + Thread.currentThread().getName() + ",運行開始");
try {
//模擬執(zhí)行一段操作,耗時4秒鐘
Thread.sleep(4000);
System.out.println("線程:" + Thread.currentThread().getName() + ",運行結(jié)束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 模擬:同一個對象下,兩個線程,同步執(zhí)行一個方法(串行執(zhí)行則為線程安全,并行執(zhí)行,則為線程不安全,)
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("測試結(jié)束");
}
}
線程:Thread-0,運行開始
線程:Thread-0,運行結(jié)束
線程:Thread-1,運行開始
線程:Thread-1,運行結(jié)束
測試結(jié)束
三.synchronized關(guān)鍵字,是怎么保證線程安全的呢?
synchronized代碼塊前時,會自動獲取到監(jiān)視器鎖,此時其他線程在訪問synchronized代碼塊時,就會被堵塞掛起(被拒之門外的意思)。拿到鎖的線程會在執(zhí)行完成、或者拋出異常、或者調(diào)用wait系列方法時釋放該鎖。其他線程只能等待鎖被釋放后才能獲取該鎖。synchronized關(guān)鍵字將代碼塊中代碼由并行轉(zhuǎn)變成了串行,這樣就保證了代碼被順序執(zhí)行。四.synchronized在內(nèi)存層面,是如何實現(xiàn)加鎖和釋放鎖的?
進入
synchronized代碼塊時,會將代碼塊內(nèi)用到的變量從該線程的工作內(nèi)存中清除,轉(zhuǎn)而從主內(nèi)存中獲取。退出
synchronized代碼塊時,會將代碼塊內(nèi)用到的變量的修改,刷新到主內(nèi)存中。
synchronized解決共享變量內(nèi)存可見性的原理。關(guān)于synchronized的性質(zhì)(可見性、可重入性),我會在后續(xù)其他文章中詳細解釋。五.synchronized將線程的并行處理轉(zhuǎn)為串行處理,有什么缺點?
synchronized將并行改為串行,當然會影響程序的執(zhí)行效率,執(zhí)行速度會受到影響。其次,synchronized操作線程的堵塞,也就是由操作系統(tǒng)控制CPU的內(nèi)核進行上下文的切換,這個切換本身也是耗時的。所以使用synchronized關(guān)鍵字會降低程序的運行效率。六. 使用Synchronized關(guān)鍵字需要注意什么?
七.總結(jié)
synchronized (共享變量) {
//需要同步的代碼
}
private synchronized void method() {
//需要同步的代碼
}
推薦閱讀:
【184期】SQL數(shù)據(jù)庫面試題以及答案(50例題)
【182期】SpringCloud常見面試題(2020最新版)
微信掃描二維碼,關(guān)注我的公眾號
朕已閱 

