鎖對象。synchronized(this)和synchronized方法都是鎖當前對象。java
import java.util.concurrent.TimeUnit; public class Test_01 { private int count = 0; private Object o = new Object(); public static void main(String[] args) { final Test_01 t = new Test_01(); new Thread(new Runnable() { @Override public void run() { t.testSync2(); } }, "testSync2").start(); new Thread(new Runnable() { @Override public void run() { t.testSync1(); } }, "testSync1").start(); new Thread(new Runnable() { @Override public void run() { t.testSync3(); } }, "testSync3").start(); } public void testSync1() { synchronized (o) { System.out.println(Thread.currentThread().getName() + " count = " + count++); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } } public void testSync2() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " count = " + count++); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void testSync3() { System.out.println(Thread.currentThread().getName() + " count = " + count++); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } }
同步方法 - static:靜態同步方法,鎖的是當前類型的類對象。在代碼中就是類名.class安全
import java.util.concurrent.TimeUnit; public class Test_02 { private static int staticCount = 0; public static synchronized void testSync4() { System.out.println(Thread.currentThread().getName() + " staticCount = " + staticCount++); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void testSync5() { synchronized (Test_02.class) { System.out.println(Thread.currentThread().getName() + " staticCount = " + staticCount++); } } }
同步方法 - 原子性
加鎖的目的: 就是爲了保證操做的原子性。ide
public class Test_03 implements Runnable { private int count = 0; public static void main(String[] args) { Test_03 t = new Test_03(); for (int i = 0; i < 5; i++) { new Thread(t, "Thread - " + i).start(); } } @Override public /*synchronized*/ void run() { System.out.println(Thread.currentThread().getName() + " count = " + count++); } }
同步方法 - 同步方法和非同步方法的調用
同步方法隻影響鎖定同一個鎖對象的同步方法。不影響其餘線程調用非同步方法,或調用其餘鎖資源的同步方法。this
public class Test_04 { Object o = new Object(); public static void main(String[] args) { Test_04 t = new Test_04(); new Thread(new Test_04.MyThread01(0, t)).start(); new Thread(new Test_04.MyThread01(1, t)).start(); new Thread(new Test_04.MyThread01(-1, t)).start(); } public synchronized void m1() { // 重量級的訪問操做。 System.out.println("public synchronized void m1() start"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("public synchronized void m1() end"); } public void m3() { synchronized (o) { System.out.println("public void m3() start"); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("public void m3() end"); } } public void m2() { System.out.println("public void m2() start"); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("public void m2() end"); } public static class MyThread01 implements Runnable { int i; Test_04 t; public MyThread01(int i, Test_04 t) { this.i = i; this.t = t; } public void run() { if (i == 0) { t.m1(); } else if (i > 0) { t.m2(); } else { t.m3(); } } } }
結果:atom
public synchronized void m1() start public void m2() start public void m3() start public void m2() end public void m3() end public synchronized void m1() end
同步方法 - 多方法調用原子性問題(業務)
同步方法只能保證當前方法的原子性,不能保證多個業務方法之間的互相訪問的原子性。
注意:在商業開發中,多方法要求結果訪問原子操做,須要多個方法都加鎖,且鎖定統一個資源。通常來講,商業項目中,不考慮業務邏輯上的髒讀問題。spa
import java.util.concurrent.TimeUnit; public class Test_05 { private double d = 0.0; public static void main(String[] args) { final Test_05 t = new Test_05(); new Thread(new Runnable() { @Override public void run() { t.m1(100); } }).start(); System.out.println(t.m2()); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(t.m2()); } public synchronized void m1(double d) { try { // 至關於複雜的業務邏輯代碼。 TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } this.d = d; } public double m2() { return this.d; } }
鎖可重入: 同一個線程,屢次調用同步代碼,鎖定同一個鎖對象,可重入。操作系統
import java.util.concurrent.TimeUnit; public class Test_06 { public static void main(String[] args) { new Test_06().m1(); } synchronized void m1() { // 鎖this System.out.println("m1 start"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } m2(); System.out.println("m1 end"); } synchronized void m2() { // 鎖this System.out.println("m2 start"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m2 end"); } }
同步方法 - 繼承::類同步方法覆蓋父類同步方法。能夠指定調用父類的同步方法。至關於鎖的重入。線程
import java.util.concurrent.TimeUnit; public class Test_07 { public static void main(String[] args) { new Sub_Test_07().m(); } synchronized void m() { System.out.println("Super Class m start"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Super Class m end"); } } class Sub_Test_07 extends Test_07 { synchronized void m() { System.out.println("Sub Class m start"); super.m(); System.out.println("Sub Class m end"); } }
同步方法 - 鎖與異常:當同步方法中發生異常的時候,自動釋放鎖資源。不會影響其餘線程的執行。注意同步業務邏輯中,若是發生異常如何處理。code
import java.util.concurrent.TimeUnit; public class Test_08 { int i = 0; public static void main(String[] args) { final Test_08 t = new Test_08(); new Thread(new Runnable() { @Override public void run() { t.m(); } }, "t1").start(); new Thread(new Runnable() { @Override public void run() { t.m(); } }, "t2").start(); } synchronized void m() { System.out.println(Thread.currentThread().getName() + " - start"); while (true) { i++; System.out.println(Thread.currentThread().getName() + " - " + i); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (i == 5) { i = 1 / 0; } } } }
結果:對象
t1 - start t1 - 1 t1 - 2 t1 - 3 t1 - 4 t1 - 5 Exception in thread "t1" java.lang.ArithmeticException: / by zero t2 - start at concurrent.t01.Test_08.m(Test_08.java:43) t2 - 6 at concurrent.t01.Test_08$1.run(Test_08.java:19) at java.base/java.lang.Thread.run(Thread.java:844) t2 - 7 t2 - 8 t2 - 9 t2 - 10
volatile的可見性:通知OS操做系統底層,在CPU計算過程當中,都要檢查內存中數據的有效性。保證最新的內存數據被使用。
import java.util.concurrent.TimeUnit; public class Test_09 { volatile boolean b = true; public static void main(String[] args) { final Test_09 t = new Test_09(); new Thread(new Runnable() { @Override public void run() { t.m(); } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t.b = false; } void m() { System.out.println("start"); while (b) { } System.out.println("end"); } }
volatile的非原子性問題:只能保證可見性,不能保證原子性。不是枷鎖問題,只是內存數據可見。
public class Test_10 { volatile int count = 0; public static void main(String[] args) { final Test_10 t = new Test_10(); List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 10; i++) { threads.add(new Thread(new Runnable() { @Override public void run() { t.m(); } })); } for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { try { thread.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(t.count); } /*synchronized*/ void m() { for (int i = 0; i < 10000; i++) { count++; } } }
同步類型:原子操做類型。 其中的每一個方法都是原子操做。能夠保證線程安全。
import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class Test_11 { AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) { final Test_11 t = new Test_11(); List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 10; i++) { threads.add(new Thread(new Runnable() { @Override public void run() { t.m(); } })); } for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { try { thread.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(t.count.intValue()); } void m() { for (int i = 0; i < 10000; i++) { /*if(count.get() < 1000)*/ count.incrementAndGet(); } } }
同步粒度問題:儘可能在商業開發中避免同步方法。使用同步代碼塊。 細粒度解決同步問題。能夠提升效率。
public class Test_12 { synchronized void m1() { // 前置邏輯 System.out.println("同步邏輯"); // 後置邏輯 } void m2() { // 前置邏輯 synchronized (this) { System.out.println("同步邏輯"); } // 後置邏輯 } }
對象變動問題:同步代碼一旦加鎖後,那麼會有一個臨時的鎖引用執行鎖對象,和真實的引用無直接關聯。在鎖未釋放以前,修改鎖對象引用,不會影響同步代碼的執行。
public class Test_13 { Object o = new Object(); int i = 0; public static void main(String[] args) { final Test_13 t = new Test_13(); new Thread(new Runnable() { @Override public void run() { t.m(); } }, "thread1").start(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } Thread thread2 = new Thread(new Runnable() { @Override public void run() { t.m(); } }, "thread2"); t.o = new Object(); thread2.start(); System.out.println(t.i); System.out.println(t.a()); System.out.println(t.i); } int a() { try { /* * return i -> * int _returnValue = i; // 0; * return _returnValue; */ return i; } finally { i = 10; } } void m() { System.out.println(Thread.currentThread().getName() + " start"); synchronized (o) { while (true) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " - " + o); } } } }
結果:
thread1 start thread1 - java.lang.Object@6018fac7 thread1 - java.lang.Object@6018fac7 thread1 - java.lang.Object@6018fac7 0 0 10 thread2 start thread1 - java.lang.Object@6edf18b4 thread2 - java.lang.Object@6edf18b4 thread1 - java.lang.Object@6edf18b4 thread2 - java.lang.Object@6edf18b4 thread1 - java.lang.Object@6edf18b4
常量問題:在定義同步代碼塊時,不要使用常量對象做爲鎖對象。
i一、i2會實現m一、m2方法的同步;s一、s2是不一樣的對象,不能實現m一、m2方法的同步。
public class Test_14 { String s1 = "hello"; String s2 = new String("hello"); // new關鍵字,必定是在堆中建立一個新的對象。 Integer i1 = 1; Integer i2 = 1; public static void main(String[] args) { final Test_14 t = new Test_14(); new Thread(new Runnable() { @Override public void run() { t.m1(); } }).start(); new Thread(new Runnable() { @Override public void run() { t.m2(); } }).start(); } void m1() { synchronized (i1) { System.out.println("m1()"); while (true) { } } } void m2() { synchronized (i2) { System.out.println("m2()"); while (true) { } } } }
能夠和鎖混合使用,或替代鎖的功能。在門閂未徹底開放以前等待。當門閂徹底開放後執行。避免鎖的效率低下問題。
import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class Test_15 { CountDownLatch latch = new CountDownLatch(5); public static void main(String[] args) { final Test_15 t = new Test_15(); new Thread(new Runnable() { @Override public void run() { t.m1(); } }).start(); new Thread(new Runnable() { @Override public void run() { t.m2(); } }).start(); } void m1() { try { latch.await();// 等待門閂開放。 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1() method"); } void m2() { for (int i = 0; i < 10; i++) { if (latch.getCount() != 0) { System.out.println("latch count : " + latch.getCount()); latch.countDown(); // 減門閂上的鎖。 } try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("m2() method : " + i); } } }
結果:
latch count : 5 m2() method : 0 latch count : 4 m2() method : 1 latch count : 3 m2() method : 2 latch count : 2 m2() method : 3 latch count : 1 m1() method m2() method : 4 m2() method : 5 m2() method : 6
...
自定義容器,提供新增元素(add)和獲取元素數量(size)方法。
啓動兩個線程。線程1向容器中新增10個數據。線程2監聽容器元素數量,當容器元素數量爲5時,線程2輸出信息並終止。
方法一(volatile的可見性):
import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; public class Test_01 { public static void main(String[] args) { final Test_01_Container t = new Test_01_Container(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("add Object to Container " + i); t.add(new Object()); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { if (t.size() == 5) { System.out.println("size = 5"); break; } } } }).start(); } } class Test_01_Container { volatile List<Object> container = new ArrayList<>(); public void add(Object o) { this.container.add(o); } public int size() { return this.container.size(); } }
方法二(synchornized):
import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; public class Test_02 { public static void main(String[] args) { final Test_02_Container t = new Test_02_Container(); final Object lock = new Object(); new Thread(new Runnable() { @Override public void run() { synchronized (lock) { if (t.size() != 5) { try { lock.wait(); // 線程進入等待隊列。 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("size = 5"); lock.notifyAll(); // 喚醒其餘等待線程 } } }).start(); new Thread(new Runnable() { @Override public void run() { synchronized (lock) { for (int i = 0; i < 10; i++) { System.out.println("add Object to Container " + i); t.add(new Object()); if (t.size() == 5) { lock.notifyAll(); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } } }).start(); } } class Test_02_Container { List<Object> container = new ArrayList<>(); public void add(Object o) { this.container.add(o); } public int size() { return this.container.size(); } }
方法三(門閂):
import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class Test_03 { public static void main(String[] args) { final Test_03_Container t = new Test_03_Container(); final CountDownLatch latch = new CountDownLatch(1); new Thread(new Runnable() { @Override public void run() { if (t.size() != 5) { try { latch.await(); // 等待門閂的開放。 不是進入等待隊列 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("size = 5"); } }).start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("add Object to Container " + i); t.add(new Object()); if (t.size() == 5) { latch.countDown(); // 門閂-1 } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } class Test_03_Container { List<Object> container = new ArrayList<>(); public void add(Object o) { this.container.add(o); } public int size() { return this.container.size(); } }