摘要:在多線程并發的情況下,有時就涉及到對于同一資源的讀寫,如果不進行一些處理,容易出現數據混亂,結果和實際不一致等問題。
在多線程并發的情況下,有時就涉及到對于同一資源的讀寫,如果不進行一些處理,容易出現數據混亂,結果和實際不一致等問題。java中可以使用synchronized關鍵字對資源鎖定。
synchronized的用法synchronized有2種用法:
1.修飾代碼塊,以某個對象為鎖,鎖的范圍是指定的代碼塊。
2.修飾方法,其實也可以等價于修飾代碼塊,比如修飾普通方法:
synchronized void doxx(){ //......... }
等價于
void doxx(){ synchronized (this){ //......... } }
以當前實體為鎖對象,整個方法都是在代碼塊中。也能修飾靜態方法:
public class Test{ static synchronized void doxx(){ //......... } }
等價于
public class Test{ static void doxx(){ synchronized (Test.class){ //......... } } }synchronized 修飾代碼塊
先舉一個反例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);//模擬處理一些業務,這樣也更容易重現問題 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()); }
執行結果:
size=162
這個結果是不確定,每次執行都可能不一樣。demo里啟動了2個線程,每個執行100次,按理list的數據量應該會是200個。這個就是本文開始提到的,多個線程讀寫同一個對象時,發生了數據異常。那么我們再用synchronized對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);//模擬處理一些業務,這樣也更容易重現問題 int randon = new Random().nextInt(); synchronized (list) {//就只改這個地方 list.add(randon); } } } catch (InterruptedException e) { e.printStackTrace(); } } }
main方法保持不變,結果如下:
size=200
可見使用synchronized關鍵字后,代碼的執行結果恢復了正常。
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);//模擬處理一些業務,這樣也更容易重現問題 int randon = new Random().nextInt(); list.add(randon); } } catch (InterruptedException e) { e.printStackTrace(); } } }
main方法不變,執行結果:
size=150
這是很多人開始接觸多線程時,會出現的錯誤,明明對run方法用了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 void run() { try { synchronized(this){//把synchronized改到這個地方 for(int i=0;i<100;i++){ Thread.sleep(10);//模擬處理一些業務,這樣也更容易重現問題 int randon = new Random().nextInt(); list.add(randon); } } } catch (InterruptedException e) { e.printStackTrace(); } } }
synchronized用在this上,所以t1.start()鎖定的是t1,t2.start()鎖定的是t2,2者鎖定的對象不同自然就沒有相應的效果。
那是不是synchronized用在方法上就沒有作用呢?當然不會,先看下面的例子:
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);//模擬處理一些業務,這樣也更容易重現問題 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()); }
執行結果:
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; } }
執行結果:
size=200
這個就是synchronized用在方法上的一個例子,鎖定的對象都是同一個SyncList,所以最終結果是正確的。因此synchronized使用上很重要的一點,是保障多個線程鎖定的對象要一致。
異常釋放鎖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); } }
執行結果:
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一直處于等待狀態,Thread-0一直執行到i=0時,程序發生異常,鎖被釋放。Thread-1就獲得了鎖開始執行。
總結1.synchronized可以修飾方法或者代碼塊。
2.盡量使用代碼塊以及減少代碼塊的范圍,有利于提高程序運行效率。
3.要注意鎖定的對象是否一致。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70702.html
摘要:線程同步方法是通過鎖來實現,每個對象都有切僅有一個鎖,這個鎖與一個特定的對象關聯,線程一旦獲取了對象鎖,其他訪問該對象的線程就無法再訪問該對象的其他非同步方法對于靜態同步方法,鎖是針對這個類的,鎖對象是該類的對象。 對實現了Runnable或者Callable接口類,可以通過多線程執行同一實例的run或call方法,那么對于同一實例中的局部變量(非方法變量)就會有多個線程進行更改或讀取...
摘要:本文對多線程基礎知識進行梳理,主要包括多線程的基本使用,對象及變量的并發訪問,線程間通信,的使用,定時器,單例模式,以及線程狀態與線程組。源碼采用構建,多線程這部分源碼位于模塊中。通知可能等待該對象的對象鎖的其他線程。 本文對多線程基礎知識進行梳理,主要包括多線程的基本使用,對象及變量的并發訪問,線程間通信,lock的使用,定時器,單例模式,以及線程狀態與線程組。 寫在前面 花了一周時...
摘要:非靜態方法以及方法內部的代碼塊持有的是同一個對象鎖,它們是同步執行的。可重入鎖使用時,當一個線程請求一個對象鎖時,再次請求該鎖是可以立即得到的。出現異常,會自動釋放鎖同步方法與同步代碼塊作用于整個方法,可能引起方法執行效率下降。 synchronize可以在多個線程操作同一個成員變量或者方法時,實現同步(或者互斥)的效果。synchronized可以作用于方法,以及方法內部的代碼塊。 ...
摘要:今天給大家總結一下,面試中出鏡率很高的幾個多線程面試題,希望對大家學習和面試都能有所幫助。指令重排在單線程環境下不會出先問題,但是在多線程環境下會導致一個線程獲得還沒有初始化的實例。使用可以禁止的指令重排,保證在多線程環境下也能正常運行。 下面最近發的一些并發編程的文章匯總,通過閱讀這些文章大家再看大廠面試中的并發編程問題就沒有那么頭疼了。今天給大家總結一下,面試中出鏡率很高的幾個多線...
摘要:多線程編程就像一個沼澤,中間遍布各種各樣的陷阱。但是在多線程編程或者說是并發編程中,有非常多的陷阱被埋在底層細節當中。線程池類中用于控制線程池狀態和線程數的控制變量就是一個類型的字段。 多線程編程就像一個沼澤,中間遍布各種各樣的陷阱。大多數開發者絕大部分時間都是在做上層應用的開發,并不需要過多地涉入底層細節。但是在多線程編程或者說是并發編程中,有非常多的陷阱被埋在底層細節當中。如果不知...
閱讀 1563·2023-04-25 15:50
閱讀 1304·2021-09-22 15:49
閱讀 2932·2021-09-22 15:06
閱讀 3570·2019-08-30 15:54
閱讀 2331·2019-08-29 11:33
閱讀 2118·2019-08-23 17:56
閱讀 2144·2019-08-23 17:06
閱讀 1293·2019-08-23 15:55