線程安全:當多個線程訪問某一個類時,這個類始終都能表現出正確的行爲,那麼這個類就是線程安全的ajax
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方法體裏面的內容。async
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類,類級別的鎖this
同步: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個特性code
髒讀:在設計咱們的程序的時候必定要考慮到問題的總體性,否則就會出現很經典的錯誤:髒讀
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執行"); } } } }