本文按照慕課網免費課程敲的,同時也加入了我大量的思考和繪圖,但願對你有所幫助java
下面WhatIsWrong實現Runnable,並提供一個
靜態實例對象
和計時器i
run方法讓i自加10W次,下面的結果是多少?git
public class WhatIsWrong implements Runnable {
static WhatIsWrong instance = new WhatIsWrong();
static int i = 0;
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
System.out.println(i);
}
@Override
public void run() {
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
複製代碼
答案是0,簡單講一下:main中代碼順序執行雖然線程1,2都開啓了,
可是程序仍是順序執行的,會馬上走System.out.println(i);
,實際看來run方法要慢一些github
兩個方法:1)
讓主線程先睡一會
、2)使用線程對象的join方法
總之就是推遲System.out.println(i);
的執行時間編程
這個方法很容易想到,但睡多久很差把握,通常小測試1s應該夠了安全
public class WhatIsWrong implements Runnable {
static WhatIsWrong instance = new WhatIsWrong();
static int i = 0;
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
@Override
public void run() {
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
複製代碼
正規的仍是用join吧,他會讓該線程先行,使以
System.out.println(i);
被推後bash
public class WhatIsWrong implements Runnable {
static WhatIsWrong instance = new WhatIsWrong();
static int i = 0;
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
@Override
public void run() {
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
複製代碼
137355 、 114412 、115381 、128482 、151021 、
109093 、 128610 、128144 、122390 、123746
複製代碼
1).每次執行結果都不同
2).它們都大於100000且小於200000微信
理論上兩個線程,每一個線程加100000次,一共應該200000纔對
想象一下這個場景:多線程
有兩個神槍手對着靶子打(必中),每把槍有100000顆軟子彈
靶子有感應器和計數器,當軟子彈接觸靶子那一刻,計數器加1
當兩個子彈同時接觸時,感應器有沒法及時反應,只會被記錄一次,即計數器只+1
而後兩人瘋狂掃射,最後看靶子上計數器最終的數值
可想而知最後計數器上的數應該是小於200000的,因此代碼中也相似
兩個線程即是神槍手,run的時候開始同時瘋狂掃射,i即是靶子
複製代碼
1)
內存中讀取i的值
,2)i=i+1
,3)將結果寫回內存
i=9時,若線程2已經在第三步了,但還沒寫入內存。這時線程1進入,讀出i的值還是9,
從而致使這次結束兩個結果都是10,這就是爲何達不到200000的緣由
這就至關於兩個神槍手同時開槍,靶子未及時反應而致使兩顆同彈併發
先看問題出在哪,是兩我的同時開槍對一個靶子
一我的是不能在同一時刻發出兩法子彈的,so,方法1:
準備兩個靶子,各自統計(像每一個足球運動員一個足球同樣,10000我的怎麼辦,然並卵)
方法2:不容許兩我的同時開槍,這即是synchronized
神槍手1在掃射時,神射手2的槍自動鎖死,若是100條線程也是相似,某一刻只能一人開槍ide
public class WhatIsWrong implements Runnable {
static WhatIsWrong instance = new WhatIsWrong();
static int i = 0;
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);//200000
}
@Override
public synchronized void run() {//只需輕輕加一個synchronized便可
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
複製代碼
先看一下幹了什麼事:線程建立不說了,run方法中:
打印信息
-->當前線程睡三秒
-->打印運行結束
(以下圖)
根據時間線能夠看出來打印結果(能夠看出兩我的一塊兒睡了,這還得了...)
public class SynObj_Block implements Runnable {
static SynObj_Block instance = new SynObj_Block();
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Finished");
}
@Override
public void run() {
System.out.println("對象鎖,代碼塊形式--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("運行結束,name:" + Thread.currentThread().getName());
}
}
複製代碼
打印結果:
對象鎖,代碼塊形式--name:Thread-1
對象鎖,代碼塊形式--name:Thread-0
運行結束,name:Thread-0
運行結束,name:Thread-1
All Finished
複製代碼
上面說兩個線程一塊兒睡了,線程1先睡,線程2進來也睡了,能忍嗎?不能忍!
快把哥的四十米大刀,不對,是大鎖拿來,在我睡覺前先把門鎖上
線程1進來睡,而後把門鎖上,線程2就進不來,只能等線程1把鎖打開
其餘代碼不變,就不貼了
@Override
public void run() {
synchronized (this) {
System.out.println("對象鎖,代碼塊形式--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("運行結束,name:" + Thread.currentThread().getName());
}
}
複製代碼
可見線程1睡完,線程2才能進來睡
對象鎖,代碼塊形式--name:Thread-0
運行結束,name:Thread-0
對象鎖,代碼塊形式--name:Thread-1
運行結束,name:Thread-1
All Finished
複製代碼
等等,這this是什麼鬼?--有點基礎的都知道是當前類對象
System.out.println(this);// top.toly.併發.SynObj_Block@77c89a74
同步代碼塊synchronized()接收一個對象,該對象可任意指定:
Object lock = new Object();
synchronized (lock) {//TODO}
新建一個對象也能夠
複製代碼
也會你會說:既然隨便一個對象均可以當作鎖對象,Java本身給內置個唄
還傳個參數,累不累人。等等,存在即合理,且看下面...
想一下若是一個房間兩張牀,你上來把門鎖了,豈不是不合理?
那該怎麼辦?兩扇門,兩把不一樣的鎖唄(就像兩我的合租一間大房子同樣)
你能夠根據圖中時間線
好好想一想(畫個圖也不是那麼容易的...且看且珍惜)
/**
* 做者:張風捷特烈
* 時間:2018/12/28 0028:19:16
* 郵箱:1981462002@qq.com
* 說明:對象鎖--代碼塊鎖
*/
public class SynObj_Block implements Runnable {
static SynObj_Block instance = new SynObj_Block();
Object lock1 = new Object();//第一把鎖
Object lock2 = new Object();//第二把鎖
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Finished");
}
@Override
public void run() {
synchronized (lock1) {
System.out.println("lock1開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("lock1結束,name:" + Thread.currentThread().getName());
}
synchronized (lock2) {
System.out.println("lock2開始,代碼塊形式--name:" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("lock2結束,name:" + Thread.currentThread().getName());
}
}
}
複製代碼
對象鎖lock1,代碼塊形式--name:Thread-0
lock1睡醒了,name:Thread-0
對象鎖lock1,代碼塊形式--name:Thread-1
對象鎖lock2,代碼塊形式--name:Thread-0
lock1睡醒了,name:Thread-1
lock2睡醒了,name:Thread-0
對象鎖lock2,代碼塊形式--name:Thread-1
lock2睡醒了,name:Thread-1
All Finished
複製代碼
有什麼好處?兩人合租房有什麼好處,多把鎖就有什麼好處。
可看出既完成任務,又減小了2秒,這也就兩個線程而已
若是百萬級的線程數,哪怕微小的效率提高都是有價值的
正如1.4所想:我就是想簡單的加個鎖,每次同步代碼塊還有傳個對象,挺煩的
因此有一個叫方法鎖,什麼對象每一個類都有?答案:this
,方法鎖的對象默認是this
/**
* 做者:張風捷特烈
* 時間:2018/12/28 0028:19:16
* 郵箱:1981462002@qq.com
* 說明:對象鎖--普通方法鎖
*/
public class SynObj_Method implements Runnable {
static SynObj_Method instance = new SynObj_Method();
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Finished");
}
@Override
public void run() {
sleep3ms();
}
public synchronized void sleep3ms() {
System.out.println("方法鎖測試--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("結束,name:" + Thread.currentThread().getName());
}
}
複製代碼
和同步代碼塊一致
方法鎖測試--name:Thread-0
結束,name:Thread-0
方法鎖測試--name:Thread-1
結束,name:Thread-1
All Finished
複製代碼
你說this就this?何以見得?
@Override
public void run() {
sleep3ms();
synchronized (this){
System.out.println("測試開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("測試結束,name:" + Thread.currentThread().getName());
}
}
public synchronized void sleep3ms() {
System.out.println("方法鎖測試--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("結束,name:" + Thread.currentThread().getName());
}
複製代碼
方法鎖開始--name:Thread-0
方法鎖結束,name:Thread-0
同步代碼塊測試開始--name:Thread-0
同步代碼塊測試結束,name:Thread-0
方法鎖開始--name:Thread-1
方法鎖結束,name:Thread-1
同步代碼塊測試開始--name:Thread-1
同步代碼塊測試結束,name:Thread-1
All Finished
複製代碼
加上this同步代碼塊後:可見開始與結束兩兩配對
說明方法鎖和同步代碼塊的this鎖是一把鎖,也就是隻有一扇門,必須一個一個睡
@Override
public void run() {
sleep3ms();
synchronized (""){
System.out.println("測試開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("測試結束,name:" + Thread.currentThread().getName());
}
}
public synchronized void sleep3ms() {
System.out.println("方法鎖測試--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("結束,name:" + Thread.currentThread().getName());
}
複製代碼
方法鎖開始--name:Thread-0
方法鎖結束,name:Thread-0
方法鎖開始--name:Thread-1
同步代碼塊測試開始--name:Thread-0
方法鎖結束,name:Thread-1
同步代碼塊測試結束,name:Thread-0
同步代碼塊測試開始--name:Thread-1
同步代碼塊測試結束,name:Thread-1
All Finished
複製代碼
若是鎖不是this,這裏簡單點用"",可見Thread-0的結束後
Thread-1的方法鎖開始
和Thread-0的同步代碼塊測試開始
是同時打印出來的
說明有兩扇門,那兩把鎖不是同一把,也反向代表,非this會產生兩把鎖
綜上正反兩面,咱們能夠感覺到方法鎖的鎖對象是this
說是類鎖,實質上是使用了Class對象當作鎖,非要較真的話,你能夠把他看做對象鎖
Class對象有什麼特色:一個類能夠有多個對象,但僅有一個
Class對象
這就能夠致使:類鎖只能在同一時刻被一個對象擁有
普通方法+synchronized
可是兩個不一樣的Runnable對象
線程
public class Syn_Static_Method implements Runnable {
static Syn_Static_Method instance1 = new Syn_Static_Method();
static Syn_Static_Method instance2 = new Syn_Static_Method();
public static void main(String[] args) {
Thread thread_1 = new Thread(instance1);
Thread thread_2 = new Thread(instance2);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Finished");
}
@Override
public void run() {
sleep3ms();
}
public synchronized void sleep3ms() {
System.out.println("靜態方法鎖開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("靜態方法鎖開始,name:" + Thread.currentThread().getName());
}
}
複製代碼
好吧,用腳趾頭想一想也知道互不影響
這至關於兩我的有兩個家,各自進各自的家睡覺天經地義
你加synchronized鎖你家的門管我什麼事,因此synchronized這時並沒用處
靜態方法鎖開始--name:Thread-1
靜態方法鎖開始--name:Thread-0
靜態方法鎖開始,name:Thread-0
靜態方法鎖開始,name:Thread-1
All Finished
複製代碼
咱們都知道static關鍵字修飾的方法、變量,是能夠令於類名(Class對象)的
也就是不須要對象即可以運行,由static修飾的方法是不能用this對象的
這就是爲何加一個static,鎖就不一樣了的緣由,至於鎖是什麼,除了它的老大還有人選嗎?
/**
* 做者:張風捷特烈
* 時間:2018/12/28 0028:19:16
* 郵箱:1981462002@qq.com
* 說明:對象鎖--靜態方法鎖
*/
public class Syn_Static_Method implements Runnable {
//同上...略
public static synchronized void sleep3ms() {//我就輕輕加個static
//同上...略
}
}
複製代碼
靜態方法鎖開始--name:Thread-0
靜態方法鎖開始,name:Thread-0
靜態方法鎖開始--name:Thread-1
靜態方法鎖開始,name:Thread-1
All Finished
複製代碼
符合預期:這樣就將一個類給鎖起來了,只要是這個類的對象
都會生效,這也是它的優點,也是static的本意:靜態,具備全局控制力
至關於把
static+synchronized
拆出來
/**
* 做者:張風捷特烈
* 時間:2018/12/28 0028:19:16
* 郵箱:1981462002@qq.com
* 說明:對象鎖--class鎖
*/
public class Syn_Class implements Runnable {
static Syn_Class instance1 = new Syn_Class();
static Syn_Class instance2 = new Syn_Class();
public static void main(String[] args) {
Thread thread_1 = new Thread(instance1);
Thread thread_2 = new Thread(instance2);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Finished");
}
@Override
public void run() {
sleep3ms();
}
public void sleep3ms() {
synchronized (Syn_Class.class) {
System.out.println("class鎖開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("class鎖開始,name:" + Thread.currentThread().getName());
}
}
}
複製代碼
class鎖開始--name:Thread-0
class鎖開始,name:Thread-0
class鎖開始--name:Thread-1
class鎖開始,name:Thread-1
All Finished
複製代碼
synchronized:
同步的
官宣:
同步方法支持一種簡單的策略來[防止線程干擾]和[內存一致性錯誤]:
若是一個對象變量對多個線程可見,則對它的全部讀寫都是經過同步方法完成的
民宣:
保證同一時刻最多隻一個線程執行該段代碼,來保證併發安全
複製代碼
感受有點...麻煩
1.兩個線程訪問一個對象的普通同步方法
2.兩個線程訪問兩個對象的普通同步方法
3.兩個線程訪問靜態同步方法
4.兩個線程分別訪問普通同步方法和非同步方法
5.兩個線程分別訪問一個對象的不一樣普通同步方法
6.兩個線程分別訪問靜態同步和非靜態同步方法
方法拋出異常後,會釋放鎖
複製代碼
二-->2
中的例子:線程1,2
訪問一個對象instance
的同步方法:sleep3ms
同一個對象,須要等待鎖的釋放,才能進入普通同步方法
二-->3-->3.1
中第一個小例子(用腳趾頭想的那個)
同一類的兩個不一樣對象的普通同步方法,對於兩個線程而言,同步是無用的
二-->3-->3.1
第二個小例子,輕輕加了個static
因爲靜態同步方法的鎖是class,鎖對該類的全部對象都有效
線程2的方法沒加鎖(非同步方法),就進來睡了唄,也沒什麼特別的
注意兩頭幾乎同時執行,測試了幾回,兩頭的前後順序不定
/**
* 做者:張風捷特烈
* 時間:2018/12/29 0029:11:31
* 郵箱:1981462002@qq.com
* 說明:兩個線程分別訪問普通同步方法和非同步方法
*/
public class SynOrNot implements Runnable {
static SynOrNot instance = new SynOrNot();
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
sleep3msSync();
} else {
sleep3msCommon();
}
}
public void sleep3msCommon() {
System.out.println("非同步方法開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("非同步方法結束,name:" + Thread.currentThread().getName());
}
public synchronized void sleep3msSync() {
System.out.println("同步方法開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("同步方法結束,name:" + Thread.currentThread().getName());
}
}
複製代碼
同步方法開始--name:Thread-0
非同步方法開始--name:Thread-1
同步方法結束,name:Thread-0
非同步方法結束,name:Thread-1
All Finished
複製代碼
因爲普通同步方法是this鎖,因此對不一樣普通同步方法鎖是一致的,都生效
/**
* 做者:張風捷特烈
* 時間:2018/12/29 0029:11:31
* 郵箱:1981462002@qq.com
* 說明:兩個線程分別訪問一個對象的不一樣普通同步方法
*/
public class SynOfTwo implements Runnable {
static SynOfTwo instance = new SynOfTwo();
public static void main(String[] args) {
Thread thread_1 = new Thread(instance);
Thread thread_2 = new Thread(instance);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
sleep3msSync1();
} else {
sleep3msSync2();
}
}
public synchronized void sleep3msSync2() {
System.out.println("sleep3msSync2方法開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sleep3msSync2結束,name:" + Thread.currentThread().getName());
}
public synchronized void sleep3msSync1() {
System.out.println("sleep3msSync1開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sleep3msSync1結束,name:" + Thread.currentThread().getName());
}
}
複製代碼
sleep3msSync1開始--name:Thread-0
sleep3msSync1結束,name:Thread-0
sleep3msSync2方法開始--name:Thread-1
sleep3msSync2結束,name:Thread-1
All Finished
複製代碼
不測試都知道:一個是class鎖,一個是this鎖,鎖不一樣,不生效
在第5個的基礎上加上static關鍵字,其他不變,結果不出所料
public static synchronized void sleep3msSync2() {
複製代碼
sleep3msSync1開始--name:Thread-0
sleep3msSync2方法開始--name:Thread-1
sleep3msSync2結束,name:Thread-1
sleep3msSync1結束,name:Thread-0
All Finished
複製代碼
能夠看出線程1拋異常後,線程2是能夠正常運行的(說明線程1的鎖已經被釋放)
就像線程1在睡覺,睡着睡着仙逝了,房東(JVM)會把它擡走,把鎖給下一我的,繼續睡...
在第5個的代碼上稍微修改:int a=1/0;//異常
public synchronized void sleep3msSync1() {
System.out.println("sleep3msSync1開始--name:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
int a=1/0;//異常
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sleep3msSync1結束,name:" + Thread.currentThread().getName());
}
複製代碼
sleep3msSync1開始--name:Thread-0
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
sleep3msSync2方法開始--name:Thread-1
at top.toly.併發.SynOfError.sleep3msSync1(SynOfError.java:54)
at top.toly.併發.SynOfError.run(SynOfError.java:33)
at java.base/java.lang.Thread.run(Thread.java:844)
sleep3msSync2結束,name:Thread-1
All Finished
複製代碼
一把鎖只能由一個線程獲取,沒拿到鎖的線程必須等待
不一樣的鎖之間互不影響(至關於進不一樣的門,互不干擾,無需等待)
不管正常執行仍是拋出異常,都會釋放鎖
可重入:同一線程外層函數獲取鎖以後,內層函數能夠直接再次獲取該鎖
好處:避免死鎖,提升封裝性
粒度:線程範圍
即synchronized修飾的同步方法內部`並不是只能`調用同步方法
複製代碼
不可中斷:好比我線程1要小睡個十萬年,那線程2就要在門等上十萬年(想走都不行)。
複製代碼
描述Java程序中各變量(線程共享變量)的訪問規則,
即在JVM中將變量存儲到內存和從內存中讀取變量的底層細節
1.全部的變量都存儲在主內存中,
2.每條線程都有本身獨立的工做內存。其保存該線程用到的變量副本(主內存變量拷貝)。
規定:
[1]線程對變量的全部操做都必須在工做內存中進行,而不能直接讀寫主內存。
[2]線程沒法直接訪問非己方工做內存中的變量,線程間變量值的傳遞須要間接經過主內存。
複製代碼
1.工做內存1操做共享變量a後
刷新到主內存
2.而後線程2從主內存中讀取
共享變量a值並拷貝
到本身的工做內存
鎖定的線程1所作的任何修改都要在釋放鎖以前從工做內存刷新到主內存
線程2拿到鎖時從主內存中拷貝須要的變量到本身的工做內存(從而實現共享變量的可見)
效率低:
鎖的釋放狀況少(只能自動釋放,或異常)
不能中斷等待鎖的線程
不靈活:
加鎖和釋放鎖的時機單一,每一個鎖只有單一的條件
沒法知道釋放成功獲取鎖
複製代碼
鎖對象不能爲空,做用域不宜過大,避免死鎖
|---鎖對象的信息是放在對象頭中,因此不能爲空
|---做用域過大,致使串行執行的代碼變多,效率降低
Lock仍是synchronized
|---儘可能使用併發包裏的原子類
|---synchronized能完成的儘可能不去Lock
|---確實須要中斷等待、靈活開解鎖或Condition可使用Lock鎖
多線程訪問同步方法的幾種狀況
複製代碼
/**
* 做者:張風捷特烈
* 時間:2018/12/29 0029:11:31
* 郵箱:1981462002@qq.com
* 說明:死鎖簡單演示
*/
public class SynKill implements Runnable {
static SynKill instance1 = new SynKill();
static SynKill instance2 = new SynKill();
public static void main(String[] args) {
Thread thread_1 = new Thread(instance1);
Thread thread_2 = new Thread(instance1);
thread_1.start();
thread_2.start();
try {
thread_1.join();
thread_2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All Finished");
}
@Override
public void run() {
sleep3msSync1();
sleep3msSync2();
}
public void sleep3msSync2() {
synchronized (instance1) {
System.out.println("sleep3msSync2方法開始--name:" + Thread.currentThread().getName());
synchronized (instance2) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("sleep3msSync2結束,name:" + Thread.currentThread().getName());
}
}
public static synchronized void sleep3msSync1() {
synchronized (instance2) {
System.out.println("sleep3msSync1方法開始--name:" + Thread.currentThread().getName());
synchronized (instance1) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("sleep3msSync1結束,name:" + Thread.currentThread().getName());
}
}
}
複製代碼
public class Decode {
private Object obj = new Object();
public void say(Thread thread) {
synchronized (obj){
}
}
}
複製代碼
I:\Java\Base\Thinking\src\top\toly\併發>javac -encoding utf-8 Decode.java
I:\Java\Base\Thinking\src\top\toly\併發>javap -verbose Decode.class
Classfile /I:/Java/Base/Thinking/src/top/toly/併發/Decode.class
Last modified 2018年12月29日; size 465 bytes
MD5 checksum 732654b709aafd523b08c943dcb1f235
Compiled from "Decode.java"
public class top.toly.併發.Decode
minor version: 0
major version: 54
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #4 // top/toly/併發/Decode
super_class: #2 // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #2.#18 // java/lang/Object."<init>":()V
#2 = Class #19 // java/lang/Object
#3 = Fieldref #4.#20 // top/toly/併發/Decode.obj:Ljava/lang/Object;
#4 = Class #21 // top/toly/併發/Decode
#5 = Utf8 obj
#6 = Utf8 Ljava/lang/Object;
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 say
#12 = Utf8 (Ljava/lang/Thread;)V
#13 = Utf8 StackMapTable
#14 = Class #22 // java/lang/Thread
#15 = Class #23 // java/lang/Throwable
#16 = Utf8 SourceFile
#17 = Utf8 Decode.java
#18 = NameAndType #7:#8 // "<init>":()V
#19 = Utf8 java/lang/Object
#20 = NameAndType #5:#6 // obj:Ljava/lang/Object;
#21 = Utf8 top/toly/併發/Decode
#22 = Utf8 java/lang/Thread
#23 = Utf8 java/lang/Throwable
{
public top.toly.併發.Decode();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #2 // class java/lang/Object
8: dup
9: invokespecial #1 // Method java/lang/Object."<init>":()V
12: putfield #3 // Field obj:Ljava/lang/Object;
15: return
LineNumberTable:
line 9: 0
line 11: 4
public void say(java.lang.Thread);
descriptor: (Ljava/lang/Thread;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=4, args_size=2
0: aload_0
1: getfield #3 // Field obj:Ljava/lang/Object;
4: dup
5: astore_2
6: monitorenter <---------------monitorenter
7: aload_2
8: monitorexit <---------------monitorexit
9: goto 17
12: astore_3
13: aload_2
14: monitorexit <---------------monitorexit
15: aload_3
16: athrow
17: return
Exception table:
from to target type
7 9 12 any
12 15 12 any
LineNumberTable:
line 14: 0
line 16: 7
line 17: 17
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 12
locals = [ class top/toly/併發/Decode, class java/lang/Thread, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
}
SourceFile: "Decode.java"
複製代碼
I:\Java\Base\Thinking\src\top\toly\併發>javap -verbose Decode.class
Classfile /I:/Java/Base/Thinking/src/top/toly/併發/Decode.class
Last modified 2018年12月29日; size 331 bytes
MD5 checksum 7963d00f1f781bc47a9700c548692617
Compiled from "Decode.java"
public class top.toly.併發.Decode
minor version: 0
major version: 54
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #4 // top/toly/併發/Decode
super_class: #2 // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #2.#15 // java/lang/Object."<init>":()V
#2 = Class #16 // java/lang/Object
#3 = Fieldref #4.#17 // top/toly/併發/Decode.obj:Ljava/lang/Object;
#4 = Class #18 // top/toly/併發/Decode
#5 = Utf8 obj
#6 = Utf8 Ljava/lang/Object;
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 say
#12 = Utf8 (Ljava/lang/Thread;)V
#13 = Utf8 SourceFile
#14 = Utf8 Decode.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Utf8 java/lang/Object
#17 = NameAndType #5:#6 // obj:Ljava/lang/Object;
#18 = Utf8 top/toly/併發/Decode
{
public top.toly.併發.Decode();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #2 // class java/lang/Object
8: dup
9: invokespecial #1 // Method java/lang/Object."<init>":()V
12: putfield #3 // Field obj:Ljava/lang/Object;
15: return
LineNumberTable:
line 9: 0
line 11: 4
public void say(java.lang.Thread);
descriptor: (Ljava/lang/Thread;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 14: 0
}
SourceFile: "Decode.java"
複製代碼
兩次的對比能夠看出:obj對象上的東西有點不同
加了synchronized
代碼塊的,obj對象頭會有monitorenter
和monitorexit
注意是加鎖時使用的對象obj的對象頭
monitorenter
和monitorexit
monitorenter次數爲0時:若線程1進入,monitorenter次數+1,線程1成爲該Monitor的全部者
若此時線程2進入,因爲Monitor的全部者非線程2,線程2只能等待,直到monitorenter次數爲0
若線程1進入同步方法後,又調用了一次其餘方法,則monitorenter次數+1,方法退出時-1(可重入)
當monitorenter次數爲0,說明:線程1的該同步方法執行完畢,將工做內存刷新到主內存,並釋放鎖
這時monitorenter次數爲0,線程2容許進入,monitorenter次數+1,線程2成爲該Monitor的全部者
複製代碼
更深的東西之後慢慢來吧,先了解個線程同步的大概,併發的內功也不是一朝一夕能成的
項目源碼 | 日期 | 備註 |
---|---|---|
V0.1 | 2018-12-29 | 燃燒吧!個人併發之魂--synchronized |
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
個人github | 個人簡書 | 個人掘金 | 我的網站 |
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持