摘要:如果拿不到鎖這個線程就會不斷的嘗試獲得這把鎖,直到拿到為止。關鍵字取得的鎖都是對象鎖而不是把一段代碼方法當成鎖。多個線程對象獲得不同的鎖他們互不影響。
線程安全:當多個線程訪問某一個類時,這個類始終都能表現出正確的行為,那么這個類就是線程安全的
synchronized: 可以在任意對象及方法上加鎖,而加鎖的這段代碼稱為“互斥區”或者“臨界區”
private int count = 5; public synchronized void run(){ count--; System.err.println(this.currentThread().getName()+" count = "+count); }
當多個線程訪問Thread的run()方法時,以排隊的形式進行處理(排隊是按照CPU分配的先后順序而定的),一個線程要執行synchronized 修飾的方法里面的代碼
1.嘗試獲得鎖
2.如果拿到鎖 執行synchronized 代碼塊內容 。如果拿不到鎖 這個線程就會不斷的嘗試獲得這把鎖,直到拿到為止。而且多個線程同時去競爭這把鎖,也就是會有鎖競爭的問題
多個線程多個鎖:每個線程都可以拿到自己的指定的鎖,分別拿到鎖以后執行synchronized方法體里面的內容。
synchronized關鍵字取得的鎖都是對象鎖 而不是把一段代碼(方法)當成鎖。
多個線程對象獲得不同的鎖 他們互不影響。
private static int num; public static synchronized void printNum(String tag){ try{ if(tag.equals("a")){ num =100; System.err.println(" tag a"); Thread.sleep(2000); } else{ num = 200; System.err.println("tag b"); } } catch(Exception e){ e.printStackTrace(); } }
在靜態方法上面加synchronized 關鍵字 表示鎖定class類,獨占class類,類級別的鎖
同步:synchronized 同步的概念就是共享。如果不共享資源,沒有必須進行同步
異步:asynchronized 異步就是獨立,相互之間不受制約。例如ajax請求
public synchronized void method1(){ try { System.err.println(Thread.currentThread().getName()); Thread.sleep(4000); } catch (Exception e) { e.printStackTrace(); } } public void method2(){ System.err.println(Thread.currentThread().getName()); }
1.a線程先持有Object對象的lock鎖 B線程如果在這個時候調用對象中的synchronized 方法則需要等待 ,也就是同步。
2.a線程先持有Object對象的lock鎖 B線程可以以異步的方式調用線程對象中的非synchronized 修飾的方法 ,不需要等待。
同步的目的是為了線程安全,對于線程安全需要滿足2個特性
原子性(同步)
可見性
臟讀:在設計我們的程序的時候一定要考慮到問題的整體性,不然就會出現很經典的錯誤:臟讀
public synchronized void setValue(String username, String password){ this.username = username; this.password = password; try{ Thread.sleep(2000); }catch(Exception e){ e.printStackTrace(); } System.err.println("setValue的最終結果是 username "+ username+",password"+password); } public void getValue(){ System.err.println("getValue方法得到username"+ username+",password"+password); }
在對一個對象的方法加鎖時候,需要考慮到業務的整體性,即在setValue/getValue方法同時加上synchronized 關鍵字保證業務的原子性
鎖重入:當一個線程獲得一個對象的鎖以后 ,再次請求此對象時可以再次獲得對象的鎖
static class Main { public int i = 10; public synchronized void operationSup(){ try { i--; System.out.println("Main print i = " + i); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } static class Sub extends Main { public synchronized void operationSub(){ try { while(i > 0) { i--; System.out.println("Sub print i = " + i); Thread.sleep(100); this.operationSup(); } } catch (InterruptedException e) { e.printStackTrace(); } } }
當涉及到父類與子類相互執行的時候 可以使用鎖重入
異常釋放鎖
private int i = 0; public synchronized void operation(){ while(true){ try { i++; Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " , i = " + i); if(i == 20){ //Integer.parseInt("a"); throw new RuntimeException(); } } catch (InterruptedException e) { e.printStackTrace(); } } }
如果一個業務分為多個子模塊去執行。彼此之間相互獨立,如果其中一個業務出現異常,采取的方式是記錄日志,其他業務不受影響繼續執行
如果一個業務分為多個子模塊去執行,彼此之間是關聯的,如果其中一個業務出現異常,采取的方式是拋出RuntimeException,及時終止業務。
減小鎖的粒度:synchronized關鍵字來優化代碼塊的執行時間
public void doLongTimeTask(){ try { System.out.println("當前線程開始:" + Thread.currentThread().getName() + ", 正在執行一個較長時間的業務操作,其內容不需要同步"); Thread.sleep(2000); synchronized(this){ System.out.println("當前線程:" + Thread.currentThread().getName() + ", 執行同步代碼塊,對其同步變量進行操作"); Thread.sleep(1000); } System.out.println("當前線程結束:" + Thread.currentThread().getName() + ", 執行完畢"); } catch (InterruptedException e) { e.printStackTrace(); } }
對任意的Object對象加鎖
public void method1(){ synchronized (this) { //對象鎖 try { System.out.println("do method1.."); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public void method2(){ //類鎖 synchronized (ObjectLock.class) { try { System.out.println("do method2.."); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } private Object lock = new Object(); public void method3(){ //任何對象鎖 synchronized (lock) { try { System.out.println("do method3.."); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }
對字符串常量加鎖 產生死循環
public void method() { //new String("字符串常量") synchronized ("字符串常量") { try { while(true){ System.out.println("當前線程 : " + Thread.currentThread().getName() + "開始"); Thread.sleep(1000); System.out.println("當前線程 : " + Thread.currentThread().getName() + "結束"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
鎖對象改變 如果鎖所對象本身不發生改變,即使是屬性改變,那么依然是同步的。 同一對象屬性的修改不會影響鎖的情況
public synchronized void changeAttributte(String name, int age) { try { System.out.println("當前線程 : " + Thread.currentThread().getName() + " 開始"); this.setName(name); this.setAge(age); System.out.println("當前線程 : " + Thread.currentThread().getName() + " 修改對象內容為: " + this.getName() + ", " + this.getAge()); Thread.sleep(2000); System.out.println("當前線程 : " + Thread.currentThread().getName() + " 結束"); } catch (InterruptedException e) { e.printStackTrace(); } }
死鎖問題:在設計程序時就應該避免雙方相互持有對方的鎖的情況
public void run() { if(tag.equals("a")){ synchronized (lock1) { try { System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入lock1執行"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入lock2執行"); } } } if(tag.equals("b")){ synchronized (lock2) { try { System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入lock2執行"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1) { System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入lock1執行"); } } } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70361.html
摘要:相比與其他操作系統包括其他類系統有很多的優點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。因為多線程競爭鎖時會引起上下文切換。減少線程的使用。很多編程語言中都有協程。所以如何避免死鎖的產生,在我們使用并發編程時至關重要。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)syn...
摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術專家我看過哪些技術類書籍。 大家好,我是...
摘要:我的是忙碌的一年,從年初備戰實習春招,年三十都在死磕源碼,三月份經歷了阿里五次面試,四月順利收到實習。因為我心理很清楚,我的目標是阿里。所以在收到阿里之后的那晚,我重新規劃了接下來的學習計劃,將我的短期目標更新成拿下阿里轉正。 我的2017是忙碌的一年,從年初備戰實習春招,年三十都在死磕JDK源碼,三月份經歷了阿里五次面試,四月順利收到實習offer。然后五月懷著忐忑的心情開始了螞蟻金...
摘要:因為多線程競爭鎖時會引起上下文切換。減少線程的使用。舉個例子如果說服務器的帶寬只有,某個資源的下載速度是,系統啟動個線程下載該資源并不會導致下載速度編程,所以在并發編程時,需要考慮這些資源的限制。 最近私下做一項目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項目不存在bug,與其懼怕,何不與其剛正面。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Jav...
摘要:并發編程實戰水平很高,然而并不是本好書。一是多線程的控制,二是并發同步的管理。最后,使用和來關閉線程池,停止其中的線程。當線程調用或等阻塞時,對這個線程調用會使線程醒來,并受到,且線程的中斷標記被設置。 《Java并發編程實戰》水平很高,然而并不是本好書。組織混亂、長篇大論、難以消化,中文翻譯也較死板。這里是一篇批評此書的帖子,很是貼切。俗話說:看到有這么多人罵你,我就放心了。 然而知...
閱讀 2572·2021-09-23 11:21
閱讀 1882·2021-09-22 15:15
閱讀 970·2021-09-10 11:27
閱讀 3440·2019-08-30 15:54
閱讀 653·2019-08-30 15:52
閱讀 1335·2019-08-30 15:44
閱讀 2349·2019-08-29 15:06
閱讀 2972·2019-08-28 18:21