多個線程在操做共享的數據且操做共享數據的線程代碼有多條。當一個線程在執行操做共享數據的多條代碼過程當中,其餘線程參與了運算。就會致使線程安全問題的產生。java
同步代碼塊原理:將多條操做共享數據的線程代碼封裝起來,當有線程在執行這些代碼的時候,其餘線程時不能夠參與運算的。必需要當前線程把這些代碼都執行完畢後,其餘線程才能夠參與運算。 安全
同步的弊端:相對下降了效率,由於同步外的線程的都會判斷同步鎖。jvm
同步的前提:同步中必須有多個線程並使用同一個鎖。ide
class Single { private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class) //這個用同一對象鎖 只能建立一個對象 { if(s==null) s = new Single(); } } return s; } }
同步鎖分爲同步代碼塊鎖、同步函數鎖,同步函數的使用的鎖是this;同步代碼塊的鎖是任意的對象。使用最多的通常是同步代碼塊。函數
使用wait、notify、notifylAlll與同步鎖配合的應用,wait()將線程添加到線程池中。notify釋放本對象線程池中的任意一個線程,notifyAll釋放本線程池中全部的線程this
class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name)// { while(flag) //喚醒時 從新判斷flag 防止flag不符合也往下運行 try{this.wait();}catch(InterruptedException e){} //在這裏喚醒 醒後上面判斷flag this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"."+this.name); flag = true; notifyAll(); //喚醒全部進程 防止 出現死鎖 可是 喚醒全部鎖 致使從新判斷 效率降低了 } public synchronized void out() { while(!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".........."+this.name); flag = false; notifyAll(); } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.set("here"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
同步鎖若是操做很差會帶來新問題:死鎖。spa
線程a、線程b都一直在while(1)中運行,當線程a持有鎖a要調用鎖b時,若是此時cpu進行線程切換換到線程b,線程b運行,線程b持有鎖b要調用鎖a,結果發現,鎖a被線程a持有無法調用就會等待系統調度切換到線程a,線程a繼續運行要調用鎖b,發現鎖b被b線程持有,也無法運行,二者就造成了死鎖。線程
class Test implements Runnable { private boolean flag; Test(boolean flag) { this.flag = flag; } public void run() { if(flag) { while(true) synchronized(MyLock.locka) //同步鎖a { System.out.println(Thread.currentThread().getName()+"1 locka."); synchronized(MyLock.lockb){ System.out.println(Thread.currentThread().getName()+"1 lockb.."); } } } else { while(true) synchronized(MyLock.lockb) //同步鎖b { System.out.println(Thread.currentThread().getName()+"2 lockb"); synchronized(MyLock.locka) { System.out.println(Thread.currentThread().getName()+"2 locka."); } } } } } class MyLock //鎖對象 { public static final Object locka = new Object(); //a鎖 public static final Object lockb = new Object(); //b鎖 } class DeadLockTest { public static void main(String[] args) { Test a = new Test(true); Test b = new Test(false); Thread t1 = new Thread(a); //線程a Thread t2 = new Thread(b); //線程b t1.start(); t2.start(); } }
因爲synchronized可能會致使死鎖,而防止死鎖的notifyAll釋放全部進程,致使效率降低,java引入lock鎖,Lock接口:替代了同步代碼塊或者同步函數。將同步的隱式鎖操做變成現實鎖操做。code
同時更爲靈活。能夠一個鎖上加上多組監視器。lock():獲取鎖。unlock():釋放鎖,一般須要定義finally代碼塊中。對象
import java.util.concurrent.locks.*; class Resource { private String name; private int count = 1; private boolean flag = false; Lock lock = new ReentrantLock(); //建立鎖對象 Condition producer_con = lock.newCondition(); //一把鎖能夠掛多個監視器 Condition consumer_con = lock.newCondition(); public void set(String name) { lock.lock(); //上鎖 try { while(flag) try{producer_con.await();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"."+this.name); flag = true; consumer_con.signal(); //激活消費者線程 這樣就能夠提升效率 } finally { lock.unlock(); //解除鎖 } } public void out() { lock.lock(); try { while(!flag) try{cousumer_con.await();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"..."+this.name); flag = false; producer_con.signal(); //激活生產者線程 } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.set("provide"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo2 { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
在lock鎖中,一個鎖能夠添加多個condition監視器,確保能夠對指定線程進行操做,提升了運行效率,await()掛起線程 signal()恢復線程。
wait與sleep區別
1,wait能夠指定時間也能夠不指定。sleep必須指定時間。
2,在同步中時,對cpu的執行權和鎖的處理不一樣。
wait:釋放執行權,釋放鎖。sleep:釋放執行權,不釋放鎖。
線程終止:控制循環一般就用定義標記來完成。若是線程處於了凍結狀態,沒法讀取標記,
可使用interrupt()方法將線程從凍結狀態強制恢復到運行狀態中來,讓線程具有cpu的執行資格。 當強制動做會發生了InterruptedException,記得要處理
class StopThread implements Runnable { private boolean flag = true; public synchronized void run() { while(flag) //線程終止 標誌 { try { wait(); } catch (InterruptedException e) //interrupt強制喚醒 產生的異常 { System.out.println(Thread.currentThread().getName()+"....."+e); flag = false; } System.out.println(Thread.currentThread().getName()+"......++++"); } } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.setDaemon(true); //守護線程 若是沒有別的線程 會自動退出 t2.start(); int num = 1; for(;;) { if(++num==50) { t1.interrupt(); // t2.interrupt(); //若是沒設置爲守護線程 須要這句話來 退出 break; } System.out.println("main...."+num); } System.out.println("over"); } }
setDaemon()守護線程,必須在線程前開啓前設置線程爲守護線程,守護線程後臺運行,當沒有前臺線程時,守護線程會自動結束而後結束jvm虛擬機。
jion()函數,線程掛起,直到該線程運行完,掛起此線程的線程才運行。