synchronized關鍵字是互斥鎖,也稱爲內部鎖ide
每一個對象都有本身的monitor(鎖標記),它是爲了分配給線程,最多一個線程能夠擁有對象的鎖測試
下面是一個例子:優化
class Thread2 implements Runnable{ private int count; //修飾成員方法,鎖的是調用它的對象,該例中也便是調用它的線程 public synchronized void run() { for (int i = 0; i < 5; i ++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
調用:this
Thread2 t2 = new Thread2(); new Thread(t2).start(); new Thread(t2).start();
下面是一個例子:編碼
class Thread3 implements Runnable { private static int count; //修飾靜態方法, 鎖的是這個類的全部對象 public static synchronized void getCounter() { for (int i = 0; i < 5; i ++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void run() { getCounter(); } }
調用:spa
Thread3 t3_0 = new Thread3(); Thread3 t3_1 = new Thread3();
new Thread(t3_0).start(); new Thread(t3_1).start();
下面是一個例子:線程
public class Synchronized { private int count; public void getCount(){ for (int i = 0; i < 5; i ++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws Exception { Thread1 t1 = new Thread1(new Synchronized()); new Thread(t1).start(); new Thread(t1).start(); } } class Thread1 implements Runnable{ private Synchronized s; public Thread1(Synchronized s) { this.s = s; } @Override public void run() { //修飾代碼塊: 鎖的是()中配置的對象 synchronized(s) { s.getCount(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Jvm須要對兩類共享數據進行保護:code
1.堆中的實例變量
2.本地方法區類變量
對於對象來講 , 有一個監視器保護實例變量; 對於類來講, 有一個監視器保護類變量對象
Jvm爲每一個對象和類關聯一個鎖,若是某個線程獲取了鎖, 在它釋放鎖以前其它的線程時不能得到一樣的鎖的;blog
對於一個對象,Jvm會維護一個加鎖計數器,線程每得到一次該對象, 計數器+1, 每釋放一次, 計數器-1,當計數器=0時,鎖就徹底釋放了
當線程須要持有多個鎖時, 就有可能發生死鎖的狀況, 好比下面這個情形:
A線程首先得到lock1,在得到lock2; B線程首先得到lock2,在得到lock1,
當A線程得到lock1時,B線程得到lock2, 因而A線程等待lock2, B線程等待lock1,
兩個線程會無限的等待,這就發生了死鎖現象
下面是一個例子:
public class Deadlock { //監視器對象 private final Object lock1 = new Object(); private final Object lock2 = new Object(); public void instanceMethod1(){ synchronized(lock1){ System.out.println("線程1: 得到lock1,等待lock2"); synchronized(lock2) { System.out.println("線程1:得到lock2"); } } } public void instanceMethod2(){ synchronized(lock2){ System.out.println("線程2: 得到lock2,等待lock1"); synchronized(lock1){ System.out.println("線程2: 得到lock1"); } } } public static void main(String[] args){ final Deadlock dld = new Deadlock(); Runnable r1 = new Runnable(){ @Override public void run(){ while(true){ dld.instanceMethod1(); try{ System.out.println("線程1: 睡眠"); Thread.sleep(1000); } catch (InterruptedException ie){ } } } }; Runnable r2 = new Runnable(){ @Override public void run(){ while(true) { dld.instanceMethod2(); try { System.out.println("線程2: 睡眠"); Thread.sleep(1000); } catch (InterruptedException ie){ } } } }; Thread thdA = new Thread(r1); Thread thdB = new Thread(r2); thdA.start(); thdB.start(); } }
控制檯輸出:
線程1: 得到lock1,等待lock2
線程2: 得到lock2,等待lock1
生產中,死鎖現象一旦發生,極可能會形成災難性的後果,咱們在編碼中應該避免死鎖現象發生
1、儘可能不要編寫在同一時刻須要持有多個鎖的代碼; 2、建立和使用一個大鎖來代替若干小鎖,並把這個鎖用於互斥,而不是用做單個對象的對象級別鎖;
synchronize採起獨佔的方式,它屬於悲觀鎖,它假設了最壞的狀況,若是持有鎖的線程延遲,其餘等待程序就會測試,程序停滯不前
在等待鎖時,線程不會當即進入阻塞狀態,而是先等一段時間看鎖是否被釋放
一個線程得到了鎖,若是在接下來沒有別的線程得到該鎖,這個鎖會偏向第一個得到它的線程,使一個線程屢次得到鎖的代價更低
屢次調用粒度過小的鎖, 不如一次調用粒度大的鎖
若是存在鎖的競爭,除了互斥量的開銷外,還會發生CAS操做,在同步期間若是沒有鎖競爭,使用輕量級鎖避免互斥量的開銷