對于很多剛剛接觸多線程編程的同學(xué)來說矢洲,可能僅僅是聽說過線程同步和線程安全這兩個(gè)名詞而已炊甲,對于具體什么線程同步、什么是線程安全琴拧,可能也只能從他的名字上面去猜測一下他的意思,至于其他的可能并沒有太多的了解嘱支,因此我決定寫一篇這樣的文章蚓胸,詳細(xì)地告訴大家挣饥,線程同步和線程安全究竟是何方神圣!
首先究竟什么是線程同步呢沛膳?在了解線程同步之前扔枫,我們先了解一下什么是同步,這有助于對線程同步的理解锹安。所謂同步短荐,就是在發(fā)出一個(gè)方法的調(diào)用時(shí),在沒有得到結(jié)果之前叹哭,這個(gè)調(diào)用就不返回忍宋,同時(shí)其它的線程也不能調(diào)用這個(gè)方法!線程同步也是類似的意思风罩,但線程同步不是說讓一個(gè)線程執(zhí)行完了再執(zhí)行其它線程糠排,一般是指讓線程中的某一些操作進(jìn)行同步就可以了、下面我舉個(gè)例子:讓大家更好地進(jìn)行理解泊交。
而在多線程的編程里乳讥,我們不可避免地會遇上到這樣的一種問題柱查,就是一些數(shù)據(jù)不能被多個(gè)線程同時(shí)訪問廓俭,比如A和B同時(shí)去商店里買糖,但商店里一共就只剩下3個(gè)糖了唉工,A說我要2個(gè)研乒,B同時(shí)也說我要2個(gè),那么此時(shí)是不是A和B中肯定有一個(gè)人買不到2個(gè)糖淋硝!如果生活中還好雹熬,還可以商量著解決!那么線程里有沒有類似商量解決的方法呢谣膳?其實(shí)多線程里面也是有類似的方法的竿报,這就先讓A或B中的一個(gè)買,假設(shè)是A先買继谚,等A先買完了烈菌,B再買!剛好同步機(jī)制就可以解決上面這個(gè)問題花履,解決讓誰先買芽世,誰后買的問題. 采用同步機(jī)制就可以保證數(shù)據(jù)在任何時(shí)候最多只有一個(gè)線程進(jìn)行訪問、從而保證了數(shù)據(jù)的安全!
等A和B處理完買糖的事之后诡壁,他們是不是想干嘛就干嘛济瓢、所以說線程同步,一般是指讓線程中的某一些操作進(jìn)行同步就可以了妹卿、
Java是如何做到線程同步的呢旺矾?
- 在需要同步的方法的 方法簽名中加上 synchronized關(guān)鍵字
- 使用synchronized關(guān)鍵字對需要進(jìn)行同步的代碼塊進(jìn)行同步
- 使用java.util.concurrent.lock包中Lock對象(JDK1.8)
1.在需要同步的方法的 方法簽名中加上 synchronized關(guān)鍵字
public class Data {
private int mVal = 0;
public void setDate(int mVal) {
this.mVal = mVal;
}
public int getDate() {
return mVal;
}
}
public class MyThread implements Runnable{
private Data mData = new Data();
@Override//讓多個(gè)線程共享一個(gè)數(shù)據(jù)
public synchronized void run() {
int tmp = mData.getDate();
++tmp;
mData.setDate(tmp);
System.out.println(Thread.currentThread().getName()+"|"+mData.getDate());
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(myThread);
thread.start();
}
}
}
//打印的結(jié)果
Thread-0|1
Thread-4|2
Thread-3|3
Thread-2|4
Thread-1|5
public class MyThread implements Runnable{
private Data mData = new Data();
@Override//注釋了synchronized關(guān)鍵字后再看結(jié)果
public /*synchronized*/ void run() {
int tmp = mData.getDate();
++tmp;
mData.setDate(tmp);
System.out.println(Thread.currentThread().getName()+"|"+mData.getDate());
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(myThread);
thread.start();
}
}
}
//注釋了synchronized關(guān)鍵字后
Thread-0|2
Thread-4|5
Thread-3|4
Thread-2|3
Thread-1|2
//需要注意的是蔑鹦,運(yùn)行的結(jié)果肯定每次都是不同的,原因:線程搶占到資源的順序
2. 使用synchronized關(guān)鍵字對需要進(jìn)行同步的代碼塊進(jìn)行同步 (Data類還是一樣)
public class MyThread implements Runnable{
private Data mData = new Data();
@Override//使用synchronized代碼塊(同步塊)
public void run() {
synchronized (this) {
int tmp = mData.getDate();
++tmp;
mData.setDate(tmp);
System.out.println(Thread.currentThread().getName()+"|"+mData.getDate());
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(myThread);
thread.start();
}
}
}
//打印的結(jié)果
Thread-0|1
Thread-4|2
Thread-3|3
Thread-2|4
Thread-1|5
3. 使用java.util.concurrent.lock包中Lock對象(JDK1.8) 【有需要的時(shí)候再補(bǔ)充】
synchronized使用時(shí)需要注意的一些地方:
被synchronized關(guān)鍵字修飾的代碼塊在被線程執(zhí)行之前箕宙,首先要拿到被同步對象的鎖举反,并且一個(gè)對象僅僅是只有一個(gè)鎖,比如上面被synchronized代碼扒吁,首先那個(gè)方法需要拿到當(dāng)前對象的鎖火鼻,如果當(dāng)前的鎖已經(jīng)被其它線程拿走了,那么還沒搶到鎖的線程將從可運(yùn)行狀態(tài)轉(zhuǎn)變?yōu)樽枞麪顟B(tài)雕崩,只有當(dāng)拿到鎖的線程執(zhí)行完同步塊的代碼后魁索,就釋放鎖,讓給別的線程的盼铁、這樣就可以保證數(shù)據(jù)的完整性粗蔚!