摘要:在多線程并發(fā)的情況下,有時(shí)就涉及到對(duì)于同一資源的讀寫,如果不進(jìn)行一些處理,容易出現(xiàn)數(shù)據(jù)混亂,結(jié)果和實(shí)際不一致等問(wèn)題。
在多線程并發(fā)的情況下,有時(shí)就涉及到對(duì)于同一資源的讀寫,如果不進(jìn)行一些處理,容易出現(xiàn)數(shù)據(jù)混亂,結(jié)果和實(shí)際不一致等問(wèn)題。java中可以使用synchronized關(guān)鍵字對(duì)資源鎖定。
synchronized的用法synchronized有2種用法:
1.修飾代碼塊,以某個(gè)對(duì)象為鎖,鎖的范圍是指定的代碼塊。
2.修飾方法,其實(shí)也可以等價(jià)于修飾代碼塊,比如修飾普通方法:
synchronized void doxx(){ //......... }
等價(jià)于
void doxx(){ synchronized (this){ //......... } }
以當(dāng)前實(shí)體為鎖對(duì)象,整個(gè)方法都是在代碼塊中。也能修飾靜態(tài)方法:
public class Test{ static synchronized void doxx(){ //......... } }
等價(jià)于
public class Test{ static void doxx(){ synchronized (Test.class){ //......... } } }synchronized 修飾代碼塊
先舉一個(gè)反例demo:
import java.util.List; import java.util.Random; public class Thread1 extends Thread{ private List list; public Thread1(List list) { this.list=list; } @Override public void run() { try { for(int i=0;i<100;i++){ Thread.sleep(10);//模擬處理一些業(yè)務(wù),這樣也更容易重現(xiàn)問(wèn)題 int randon = new Random().nextInt(); list.add(randon); } } catch (InterruptedException e) { e.printStackTrace(); } } }
public static void main(String[] args) throws InterruptedException { List list = new ArrayList<>(1000); Thread1 t1 = new Thread1(list); t1.start(); Thread1 t2 = new Thread1(list); t2.start(); t1.join(); t2.join(); System.out.println("size="+list.size()); }
執(zhí)行結(jié)果:
size=162
這個(gè)結(jié)果是不確定,每次執(zhí)行都可能不一樣。demo里啟動(dòng)了2個(gè)線程,每個(gè)執(zhí)行100次,按理list的數(shù)據(jù)量應(yīng)該會(huì)是200個(gè)。這個(gè)就是本文開始提到的,多個(gè)線程讀寫同一個(gè)對(duì)象時(shí),發(fā)生了數(shù)據(jù)異常。那么我們?cè)儆胹ynchronized對(duì)demo進(jìn)行小小的改造。
import java.util.List; import java.util.Random; public class Thread1 extends Thread{ private List list; public Thread1(List list) { this.list=list; } @Override public void run() { try { for(int i=0;i<100;i++){ Thread.sleep(10);//模擬處理一些業(yè)務(wù),這樣也更容易重現(xiàn)問(wèn)題 int randon = new Random().nextInt(); synchronized (list) {//就只改這個(gè)地方 list.add(randon); } } } catch (InterruptedException e) { e.printStackTrace(); } } }
main方法保持不變,結(jié)果如下:
size=200
可見使用synchronized關(guān)鍵字后,代碼的執(zhí)行結(jié)果恢復(fù)了正常。
synchronized修飾方法import java.util.List; import java.util.Random; public class Thread1 extends Thread{ private List list; public Thread1(List list) { this.list=list; } public synchronized void run() { try { for(int i=0;i<100;i++){ Thread.sleep(10);//模擬處理一些業(yè)務(wù),這樣也更容易重現(xiàn)問(wèn)題 int randon = new Random().nextInt(); list.add(randon); } } catch (InterruptedException e) { e.printStackTrace(); } } }
main方法不變,執(zhí)行結(jié)果:
size=150
這是很多人開始接觸多線程時(shí),會(huì)出現(xiàn)的錯(cuò)誤,明明對(duì)run方法用了synchronized 關(guān)鍵字怎么出來(lái)的結(jié)果是不對(duì)的。根據(jù)上面提到的我們把代碼轉(zhuǎn)變一下:
import java.util.List; import java.util.Random; public class Thread1 extends Thread{ private List list; public Thread1(List list) { this.list=list; } public void run() { try { synchronized(this){//把synchronized改到這個(gè)地方 for(int i=0;i<100;i++){ Thread.sleep(10);//模擬處理一些業(yè)務(wù),這樣也更容易重現(xiàn)問(wèn)題 int randon = new Random().nextInt(); list.add(randon); } } } catch (InterruptedException e) { e.printStackTrace(); } } }
synchronized用在this上,所以t1.start()鎖定的是t1,t2.start()鎖定的是t2,2者鎖定的對(duì)象不同自然就沒(méi)有相應(yīng)的效果。
那是不是synchronized用在方法上就沒(méi)有作用呢?當(dāng)然不會(huì),先看下面的例子:
import java.util.Random; public class Thread1 extends Thread{ private SyncList list; public Thread1(SyncList list) { this.list=list; } public void run() { try { for(int i=0;i<100;i++){ Thread.sleep(10);//模擬處理一些業(yè)務(wù),這樣也更容易重現(xiàn)問(wèn)題 int randon = new Random().nextInt(); list.addList(randon);//注意這里 } } catch (InterruptedException e) { e.printStackTrace(); } } }
import java.util.ArrayList; import java.util.List; public class SyncList{ private List list; public SyncList(List list) { this.list = list; } public void addList(E obj){ list.add(obj); } public List getList() { return list; } }
public static void main(String[] args) throws InterruptedException { SyncList list = new SyncList(new ArrayList<>(1000)); Thread1 t1 = new Thread1(list); t1.start(); Thread1 t2 = new Thread1(list); t2.start(); t1.join(); t2.join(); System.out.println("size="+list.getList().size()); }
執(zhí)行結(jié)果:
size=161
修改一下SyncList:
import java.util.ArrayList; import java.util.List; public class SyncList{ private List list; public SyncList(List list) { this.list = list; } public synchronized void addList(E obj){//僅在這里加上synchronized list.add(obj); } public List getList() { return list; } }
執(zhí)行結(jié)果:
size=200
這個(gè)就是synchronized用在方法上的一個(gè)例子,鎖定的對(duì)象都是同一個(gè)SyncList,所以最終結(jié)果是正確的。因此synchronized使用上很重要的一點(diǎn),是保障多個(gè)線程鎖定的對(duì)象要一致。
異常釋放鎖public class Thread1 extends Thread{ private Object lock; public Thread1(Object lock) { this.lock=lock; } public void run() { try { synchronized (lock) { for(int i=5;i>-1;i--){ System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(200); int j= 100/i; } } } catch (InterruptedException e) { e.printStackTrace(); } } }
public static void main(String[] args) throws InterruptedException { Object lock = new Object(); for(int i=0;i<2;i++){ Thread1 t1 = new Thread1(lock); t1.start(); t1.join(); Thread.sleep(10); } }
執(zhí)行結(jié)果:
Thread-0:5 Thread-0:4 Thread-0:3 Thread-0:2 Thread-0:1 Thread-0:0 Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero at Thread1.run(Thread1.java:12) Thread-1:5 Thread-1:4 Thread-1:3 Thread-1:2 Thread-1:1 Thread-1:0 Exception in thread "Thread-1" java.lang.ArithmeticException: / by zero at Thread1.run(Thread1.java:12)
可以看到由于Thread-0先獲取鎖,Thread-1一直處于等待狀態(tài),Thread-0一直執(zhí)行到i=0時(shí),程序發(fā)生異常,鎖被釋放。Thread-1就獲得了鎖開始執(zhí)行。
總結(jié)1.synchronized可以修飾方法或者代碼塊。
2.盡量使用代碼塊以及減少代碼塊的范圍,有利于提高程序運(yùn)行效率。
3.要注意鎖定的對(duì)象是否一致。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/70702.html
摘要:線程同步方法是通過(guò)鎖來(lái)實(shí)現(xiàn),每個(gè)對(duì)象都有切僅有一個(gè)鎖,這個(gè)鎖與一個(gè)特定的對(duì)象關(guān)聯(lián),線程一旦獲取了對(duì)象鎖,其他訪問(wèn)該對(duì)象的線程就無(wú)法再訪問(wèn)該對(duì)象的其他非同步方法對(duì)于靜態(tài)同步方法,鎖是針對(duì)這個(gè)類的,鎖對(duì)象是該類的對(duì)象。 對(duì)實(shí)現(xiàn)了Runnable或者Callable接口類,可以通過(guò)多線程執(zhí)行同一實(shí)例的run或call方法,那么對(duì)于同一實(shí)例中的局部變量(非方法變量)就會(huì)有多個(gè)線程進(jìn)行更改或讀取...
摘要:本文對(duì)多線程基礎(chǔ)知識(shí)進(jìn)行梳理,主要包括多線程的基本使用,對(duì)象及變量的并發(fā)訪問(wèn),線程間通信,的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。源碼采用構(gòu)建,多線程這部分源碼位于模塊中。通知可能等待該對(duì)象的對(duì)象鎖的其他線程。 本文對(duì)多線程基礎(chǔ)知識(shí)進(jìn)行梳理,主要包括多線程的基本使用,對(duì)象及變量的并發(fā)訪問(wèn),線程間通信,lock的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。 寫在前面 花了一周時(shí)...
摘要:非靜態(tài)方法以及方法內(nèi)部的代碼塊持有的是同一個(gè)對(duì)象鎖,它們是同步執(zhí)行的??芍厝腈i使用時(shí),當(dāng)一個(gè)線程請(qǐng)求一個(gè)對(duì)象鎖時(shí),再次請(qǐng)求該鎖是可以立即得到的。出現(xiàn)異常,會(huì)自動(dòng)釋放鎖同步方法與同步代碼塊作用于整個(gè)方法,可能引起方法執(zhí)行效率下降。 synchronize可以在多個(gè)線程操作同一個(gè)成員變量或者方法時(shí),實(shí)現(xiàn)同步(或者互斥)的效果。synchronized可以作用于方法,以及方法內(nèi)部的代碼塊。 ...
摘要:今天給大家總結(jié)一下,面試中出鏡率很高的幾個(gè)多線程面試題,希望對(duì)大家學(xué)習(xí)和面試都能有所幫助。指令重排在單線程環(huán)境下不會(huì)出先問(wèn)題,但是在多線程環(huán)境下會(huì)導(dǎo)致一個(gè)線程獲得還沒(méi)有初始化的實(shí)例。使用可以禁止的指令重排,保證在多線程環(huán)境下也能正常運(yùn)行。 下面最近發(fā)的一些并發(fā)編程的文章匯總,通過(guò)閱讀這些文章大家再看大廠面試中的并發(fā)編程問(wèn)題就沒(méi)有那么頭疼了。今天給大家總結(jié)一下,面試中出鏡率很高的幾個(gè)多線...
摘要:多線程編程就像一個(gè)沼澤,中間遍布各種各樣的陷阱。但是在多線程編程或者說(shuō)是并發(fā)編程中,有非常多的陷阱被埋在底層細(xì)節(jié)當(dāng)中。線程池類中用于控制線程池狀態(tài)和線程數(shù)的控制變量就是一個(gè)類型的字段。 多線程編程就像一個(gè)沼澤,中間遍布各種各樣的陷阱。大多數(shù)開發(fā)者絕大部分時(shí)間都是在做上層應(yīng)用的開發(fā),并不需要過(guò)多地涉入底層細(xì)節(jié)。但是在多線程編程或者說(shuō)是并發(fā)編程中,有非常多的陷阱被埋在底層細(xì)節(jié)當(dāng)中。如果不知...
閱讀 1879·2023-04-25 15:50
閱讀 1363·2021-09-22 15:49
閱讀 3082·2021-09-22 15:06
閱讀 3754·2019-08-30 15:54
閱讀 2392·2019-08-29 11:33
閱讀 2174·2019-08-23 17:56
閱讀 2298·2019-08-23 17:06
閱讀 1351·2019-08-23 15:55