java concurrent之前戏synchronized
對(duì)于多線程共享資源的情況須要進(jìn)行同步,以避免一個(gè)線程的修改被還有一個(gè)線程的修改所覆蓋。
最普遍的同步方式就是synchronized。
把代碼聲明為synchronized。有兩個(gè)重要后果,一般是指該代碼具有 原子性(atomicity)和 可見(jiàn)性(visibility)。
1、原子性強(qiáng)調(diào)的是運(yùn)行。意味著個(gè)時(shí)刻,僅僅有一個(gè)線程可以運(yùn)行一段代碼,這段代碼通過(guò)一個(gè)monitor object保護(hù)。從而防止多個(gè)線程在更新共享狀態(tài)時(shí)相互沖突。
2、可見(jiàn)性強(qiáng)調(diào)的是結(jié)果。它要對(duì)付內(nèi)存緩存和編譯器優(yōu)化的各種反常行為。它必須確保釋放鎖之前對(duì)共享數(shù)據(jù)做出的更改對(duì)于隨后獲得該鎖的還有一個(gè)線程是可見(jiàn)的。
同步方法
看一個(gè)樣例就明確了:
import java.util.Random;
public class TestSyncClass {
?private int num=0;
?
?private Random random=new Random();
?
?public synchronized void testAdd1(){
??System.out.println("testAdd1--->>");
??num++;
??try {
???Thread.sleep(1000);
??} catch (InterruptedException e) {
???e.printStackTrace();
??}
??System.out.println("1-result-->>"+num);
?}
?
?public synchronized void testAdd2(){
??System.out.println("testAdd2--->>");
??num++;
??try {
???Thread.sleep(1000);
??} catch (InterruptedException e) {
???e.printStackTrace();
??}
??System.out.println("2-result-->>"+num);
?}
?
?public? void testAdd3(){
??System.out.println("testAdd3--->>");
??num++;
??try {
???Thread.sleep(1000);
??} catch (InterruptedException e) {
???e.printStackTrace();
??}
??System.out.println("3-result-->>"+num);
?}
?
?public static void main(String[] args) {
??final TestSyncClass syncClass=new TestSyncClass();
??Thread thread1=new Thread(){
???@Override
???public void run() {
????syncClass.testAdd1();
????super.run();
???}
??};
??
??Thread thread2=new Thread(){
???@Override
???public void run() {
????syncClass.testAdd2();
????super.run();
???}
??};
??
??Thread thread3=new Thread(){
???@Override
???public void run() {
????syncClass.testAdd3();
????super.run();
???}
??};
??
??thread1.start();
??thread2.start();
??thread3.start();
?}
}
代碼執(zhí)行結(jié)果:
testAdd1--->>
testAdd3--->>
1-result-->>2
3-result-->>2
testAdd2--->>
2-result-->>3
代碼中testAdd1、testAdd2是被synchronized聲明的方法。testAdd3沒(méi)有聲明。在執(zhí)行的時(shí)候因?yàn)閠estAdd3沒(méi)有被聲明,所以在緊跟著開(kāi)始執(zhí)行testAdd1的時(shí)候也執(zhí)行了testAdd3。結(jié)果testAdd1執(zhí)行的結(jié)果被testAdd3的結(jié)果覆蓋了,打印了同樣的值3。這個(gè)主要是可見(jiàn)性的問(wèn)題。因?yàn)閠estAdd2也是被聲明過(guò)的,所以testAdd2并沒(méi)有馬上執(zhí)行。而是等testAdd1執(zhí)行完之后才開(kāi)始執(zhí)行。
全部對(duì)象都自己主動(dòng)含有單一的鎖(也稱為監(jiān)視器monitor object)。
當(dāng)在對(duì)象上調(diào)用其隨意synchronized方法的時(shí)候,此對(duì)象都被加鎖。這時(shí)該對(duì)象上的其它synchronized方法僅僅有等到前一個(gè)方法調(diào)用完成并釋放了鎖之后才干被調(diào)用。
針對(duì)每一個(gè)類,也有一個(gè)鎖(作為類的Class對(duì)象的一部分)。所以synchronized static 方法能夠在類的范圍內(nèi)防止對(duì)static數(shù)據(jù)的并發(fā)訪問(wèn)。
同步塊
不管何種情況,要想鎖住代碼,必須使用同樣的鎖。比如把testAdd2改成
??????? private Object object=new Object();
?public void testAdd2(){
??synchronized(object){
???System.out.println("testAdd2--->>");
???num++;
???try {
????Thread.sleep(1000);
???} catch (InterruptedException e) {
????e.printStackTrace();
???}
???System.out.println("2-result-->>"+num);
??}
?}
則testAdd2和testAdd1就不會(huì)相互等待了。結(jié)果例如以下:
testAdd2--->>
testAdd3--->>
testAdd1--->>
3-result-->>3
2-result-->>3
1-result-->>3
事實(shí)上synchronized(object)是更安全的上鎖方式。由于直接聲明方法的形式用的是類的鎖,而聲明代碼塊的形式用的是私有屬性的鎖,尤其是在server開(kāi)發(fā)的時(shí)候,外面類的鎖非常easy被黑客獲取。從而獲取了攻擊server的入口,而私有屬性的私有性讓黑客難以獲取,所以它的鎖就相對(duì)安全的多。
類同步
上面的main方法的三個(gè)線程用的是同一個(gè)TestSyncClass syncClass對(duì)象。假設(shè)每一個(gè)線程都各自創(chuàng)建一個(gè)對(duì)象就不能達(dá)到鎖定代碼的目標(biāo)了。要想達(dá)到同步的目的,代碼須要改動(dòng)成例如以下:
import java.util.Random;
public class TestSyncClass {
?private int num = 0;
?private static Object object = new Object();
?private Random random = new Random();
?public void testAdd1() {
??synchronized (object) {
???System.out.println("testAdd1--->>");
???num++;
???try {
????Thread.sleep(1000);
???} catch (InterruptedException e) {
????e.printStackTrace();
???}
???System.out.println("1-result-->>" + num);
??}
?}
?public void testAdd2() {
??synchronized (object) {
???System.out.println("testAdd2--->>");
???num++;
???try {
????Thread.sleep(1000);
???} catch (InterruptedException e) {
????e.printStackTrace();
???}
???System.out.println("2-result-->>" + num);
??}
?}
?public void testAdd3() {
??System.out.println("testAdd3--->>");
??num++;
??try {
???Thread.sleep(1000);
??} catch (InterruptedException e) {
???e.printStackTrace();
??}
??System.out.println("3-result-->>" + num);
?}
?public static void main(String[] args) {
??Thread thread1 = new Thread() {
???@Override
???public void run() {
????TestSyncClass syncClass = new TestSyncClass();
????syncClass.testAdd1();
????super.run();
???}
??};
??Thread thread2 = new Thread() {
???@Override
???public void run() {
????TestSyncClass syncClass = new TestSyncClass();
????syncClass.testAdd2();
????super.run();
???}
??};
??Thread thread3 = new Thread() {
???@Override
???public void run() {
????TestSyncClass syncClass = new TestSyncClass();
????syncClass.testAdd3();
????super.run();
???}
??};
??thread1.start();
??thread2.start();
??thread3.start();
?}
}
執(zhí)行結(jié)果:
testAdd1--->>
testAdd3--->>
3-result-->>1
1-result-->>1
testAdd2--->>
2-result-->>1
事實(shí)上使用synchronized (TestSyncClass.class)類的鎖也能達(dá)到類似的效果,可是考慮到私有屬性的安全性就直接使用上面代碼做實(shí)例了。
注意:synchronized是不能繼承的,父類中synchronized的聲明在子類的繼承過(guò)程中須要再次聲明,否則synchronized將會(huì)丟失。
wait(), notify()。notifyAll()
基類不光有鎖。還有這三個(gè)方法。wait()會(huì)讓獲取鎖的線程等待并釋放鎖,直到notify()或notifyAll()喚醒并又一次獲取鎖。
先看一個(gè)樣例:
public class TestSyncClass {
?private int num = 0;
?private Object object = new Object();
?private Object object1 = new Object();
?public? void testAdd(int index) {
??System.out.println("testAdd" + index + "--->>");
??synchronized (object) {
???num++;
???try {
????object.wait();
???} catch (InterruptedException e) {
????e.printStackTrace();
???}
???System.out.println(index + "-result-->>" + num);
??}
?}
?public void release() {
??synchronized (object) {
???object.notifyAll();
???System.out.println("-release-->>");
??}
?}
?public static void main(String[] args) {
??final TestSyncClass syncClass = new TestSyncClass();
??Thread thread1 = new Thread() {
???@Override
???public void run() {
????syncClass.testAdd(1);
????super.run();
???}
??};
??Thread thread2 = new Thread() {
???@Override
???public void run() {
????syncClass.testAdd(2);
????super.run();
???}
??};
??Thread thread3 = new Thread() {
???@Override
???public void run() {
????syncClass.testAdd(3);
????super.run();
???}
??};
??thread1.start();
??thread2.start();
??thread3.start();
??Thread thread4 = new Thread() {
???@Override
???public void run() {
????try {
?????Thread.sleep(2000);
????} catch (InterruptedException e) {
?????e.printStackTrace();
????}
????syncClass.release();
????super.run();
???}
??};
??thread4.start();
?}
}
執(zhí)行結(jié)果:
testAdd1--->>
testAdd2--->>
testAdd3--->>
-release-->>
3-result-->>3
2-result-->>3
1-result-->>3
調(diào)用object的wait(), notify()。notifyAll()法前,必須獲得object鎖,也就是這三個(gè)方法必須寫在synchronized(obj) {…} 代碼段內(nèi)。否則跑出異常java.lang.IllegalMonitorStateException。
調(diào)用object.wait()后。線程A就釋放了object的鎖,否則syncClass.release()無(wú)法獲得object鎖,等待的線程。
當(dāng)object.wait()方法返回后。各個(gè)線程須要再次獲得object鎖,才干繼續(xù)運(yùn)行。
notify()僅僅能喚醒線程,notifyAll()則能所有喚醒,可是個(gè)線程須要又一次競(jìng)爭(zhēng)object的鎖。
?
?
總結(jié)
以上是生活随笔為你收集整理的java concurrent之前戏synchronized的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 介绍Linux中cp直接覆盖不提示的方法
- 下一篇: nohup启动jar_nohup命令详解