1.synchronized的作用:同步方法支持一種簡單的策略來防止線程干擾和內存一致性的錯誤:如果一個對象對多個線程可見,則對該對象的所有讀取或寫入都是通過同步方法來完成的。總的來說是能夠在同一時候保證最多只有一個線程執行該段代碼,以達到安全的效果。
2.synchronized是java的關鍵字,被java語言原生支持,是最基本的互斥同步手段
代碼演示
public class DisapperRequest1 implements Runnable {
static int k=0;
static DisapperRequest1 disapperRequest1=new DisapperRequest1();
@Override
public void run() {
for(int i=0;i<100000;i++)
{
k++;
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(disapperRequest1);
Thread thread1=new Thread(disapperRequest1);
thread.start();
thread1.start();
/**join的意思是使得放棄當前線程的執行,並返回對應的線程,例如下面代碼的意思就是: 程序在main線程中調用t1線程的join方法,則main線程放棄cpu控制權,並返回t1線程繼續執行直到線程t1執行完畢 所以結果是t1線程執行完后,才到主線程執行,相當於在main線程中同步t1線程,t1執行完了,main線程才有執行的機會
thread.join();
thread1.join();
System.out.println(k);
}
}
join方法是通過調用線程的wait方法來達到同步的目的的。例如,A線程中調用了B線程的join方法,則相當於A線程調用了B線程的wait方法,在調用了B線程的wait方法后,A線程就會進入阻塞狀態
最后的結果總是小於或等於20W 這跟預期的結果不一樣。count++看上去只有一個操作,實際上是有三個操作的1.讀取count的值 2.講count加1 3.將count的值加入內存
當count的值還沒有加入內存的時候count就已經讀取了。這樣就會造成線程不安全。
3.Synchronized兩種方法用法:對象鎖:包括方法鎖(默認鎖對象為this當前的實例的對象)和同步代碼塊鎖(自己指定鎖對象)。類鎖:指的是用Synchronized修飾的靜態方法或者指定鎖對象為class對象
代碼塊形式要手動指定對象,方發鎖形式:Synchronized修飾普通方法,鎖對象默認為this
代碼塊鎖:isAlive方法判斷線程是否存活
public class SynchronizedObjectCodeBlock2 implements Runnable { static SynchronizedObjectCodeBlock2 instance=new SynchronizedObjectCodeBlock2(); @Override public void run() { synchronized(this) { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "運行結束"); } } public static void main(String[] args) { Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); while (t1.isAlive()||t2.isAlive()) { } System.out.println("結束"); } }
當用兩個鎖的情況:當不是同一把鎖的時候,可以並行運行
public class SynchronizedObjectCodeBlock2 implements Runnable { static SynchronizedObjectCodeBlock2 instance=new SynchronizedObjectCodeBlock2(); Object lock1=new Object(); Object lock2=new Object(); @Override public void run() { synchronized(lock1) { System.out.println(Thread.currentThread().getName()+"lock1"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "運行結束1"); } synchronized(lock2) { System.out.println(Thread.currentThread().getName()+"lock2"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "運行結束2"); } } public static void main(String[] args) { Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); while (t1.isAlive()||t2.isAlive()) { } System.out.println("結束"); } }
對象鎖:Synchronized修飾普通的方法,默認鎖對象為this
package com.xiaoqiang.sychronized; public class SynchronizedObjectMethod3 implements Runnable { static SynchronizedObjectMethod3 instance=new SynchronizedObjectMethod3(); @Override public void run() { method(); } public static void main(String[] args) { Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); while (t1.isAlive()||t2.isAlive()) { } System.out.println("結束"); } public synchronized void method() { System.out.println(Thread.currentThread().getName()+"lock1"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "運行結束1"); } }
類鎖:
1.staic 類鎖:假如不在method中加入staic修飾的話,兩個不同的靜態對象就會並行執行run方法
package com.xiaoqiang.sychronized; public class SynchronizedClassStatic4 implements Runnable { static SynchronizedClassStatic4 instance1=new SynchronizedClassStatic4(); static SynchronizedClassStatic4 instance2=new SynchronizedClassStatic4(); @Override public void run() { method(); } public static void main(String[] args) { Thread t1=new Thread(instance1); Thread t2=new Thread(instance2); t1.start(); t2.start(); while (t1.isAlive()||t2.isAlive()) { } System.out.println("結束"); } public synchronized void method() { System.out.println(Thread.currentThread().getName()+"lock1"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "運行結束1"); } }
如果用 static 修飾method方法的話就會串行進行
package com.xiaoqiang.sychronized; public class SynchronizedClassStatic4 implements Runnable { static SynchronizedClassStatic4 instance1=new SynchronizedClassStatic4(); static SynchronizedClassStatic4 instance2=new SynchronizedClassStatic4(); @Override public void run() { method(); } public static void main(String[] args) { Thread t1=new Thread(instance1); Thread t2=new Thread(instance2); t1.start(); t2.start(); while (t1.isAlive()||t2.isAlive()) { } System.out.println("結束"); } public static synchronized void method() { System.out.println(Thread.currentThread().getName()+"lock1"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "運行結束1"); } }
2.。*.class 類鎖
同一個類鎖的實現
package com.xiaoqiang.sychronized; public class SynchronizedClassClass5 implements Runnable { static SynchronizedClassClass5 instance1=new SynchronizedClassClass5(); static SynchronizedClassClass5 instance2=new SynchronizedClassClass5(); @Override public void run() { method(); } public static void main(String[] args) { Thread t1=new Thread(instance1); Thread t2=new Thread(instance2); t1.start(); t2.start(); while (t1.isAlive()||t2.isAlive()) { } System.out.println("結束"); } private void method() { synchronized (SynchronizedClassClass5.class) { System.out.println(Thread.currentThread().getName() + "lock1"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "運行結束1"); } } }
4.Synchronized的核心思想:
1.一把鎖只能同時被一個線程獲取,沒有拿到鎖的線程必須等待。
2.每個實例都對應自己的一把鎖,不同實例之間互不影響。例外:鎖對象是*.class或者用Synchronized修飾的是static方法時,所有對象都共用同一把鎖
3.無論是正常運完畢或者方法拋出異常,都會釋放鎖。
5.Synchronized的性質
1.可重入性:指的是同一線程外層函數獲取到鎖后之后,內層函數可以直接再次獲取該鎖。
好處:避免死鎖,提高封裝性。
粒度(范圍):線程而非調用
2.不可中斷性:一旦這個鎖已經被別人獲得了,如果我還想獲得,就必須等待或者阻塞,直到別的線程釋放這個鎖。如果別人永遠不釋放鎖,那么只能永遠的等待下去。
6.Synchronized的缺點:
效率低:鎖的釋放情況少,試圖獲得鎖的時候不能設置超時,不能中斷一個正試圖獲得鎖的線程。
不夠靈活:加鎖和釋放鎖的時機單一,每個鎖僅有單一的條件,可能是不夠的。
無法知道是否成功獲取到鎖