原子性提供了程序的互斥操做,同一時刻只能有一個線程能對某塊代碼進行操做。java
在jdk中,原子性的實現方式主要分爲:git
synchronized
:關鍵詞,它依賴於JVM,保證了同一時刻只能有一個線程對做用對象的做用範圍內進行操做。Lock
:代碼層面的鎖,依賴CPU指令,主要實現類ReentrantLock
,以後再說。atomic
包:該包中提供了一些能夠保證原子操做的類。注意上面加粗內容,比較關鍵,後面會有講解,着重理解多線程
從性能上看,上面三個逐優,但各自又有不一樣。函數
synchronized
是不可中斷鎖,適合競爭不激烈,可讀性比較好。Lock
是可中斷鎖,多樣化同步,競爭激烈時能維持常態。atomic
競爭激烈時能維持常態,比Lock性能好,但只能同步一個值。synchronized
修飾的代碼塊,做用於調用的對象synchronized
修飾的方法,做用於調用的對象synchronized
修飾的靜態方法,做用於這個類的全部對象synchronized
修飾的類,做用於這個類的全部對象package cn.com.dotleo.sync; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by liufei on 2018/6/24. */ public class Sync1 { public void test1(int x) { synchronized (this) { for (int i = 0; i < 10; i++) { System.out.println("["+ x + "]test1: " + i); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { final Sync1 sync = new Sync1(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { public void run() { sync.test1(1); } }); executorService.execute(new Runnable() { public void run() { sync.test1(2); } }); } }
代碼參見Sync1.java性能
上面代碼中,主要體現了多線程下,同一個對象調用同步代碼塊。this
運行結果:atom
[1]test1: 0 [1]test1: 1 [1]test1: 2 [1]test1: 3 [1]test1: 4 [1]test1: 5 [1]test1: 6 [1]test1: 7 [1]test1: 8 [1]test1: 9 [2]test1: 0 [2]test1: 1 [2]test1: 2 [2]test1: 3 [2]test1: 4 [2]test1: 5 [2]test1: 6 [2]test1: 7 [2]test1: 8 [2]test1: 9
由此結果能夠看出,在多線程下,兩個線程同時做用於一個對象時,該代碼塊被先搶到資源的進程執行結束後另外一個線程才得以執行。線程
將主函數修改以下:code
public static void main(String[] args) { final Sync1 sync1 = new Sync1(); final Sync1 syncNew = new Sync1(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { public void run() { sync1.test1(1); } }); executorService.execute(new Runnable() { public void run() { syncNew.test1(2); } }); }
代碼參見Sync1.java對象
[1]test1: 0 [2]test1: 0 [1]test1: 1 [2]test1: 1 [1]test1: 2 [2]test1: 2 [1]test1: 3 [2]test1: 3 [1]test1: 4 [2]test1: 4 [1]test1: 5 [2]test1: 5 [1]test1: 6 [2]test1: 6 [1]test1: 7 [2]test1: 7 [1]test1: 8 [2]test1: 8 [2]test1: 9 [1]test1: 9
從結果中能夠看出,對於兩個線程分別調用兩個對象時,並無鎖的出現,線程交叉執行並輸出。
同理,synchronized
關鍵詞修飾方法也是如此,這裏再也不演示,代碼參見Sync2.java
所以咱們能夠看出:
synchronized
修飾的代碼塊,做用於調用的對象synchronized
修飾的方法,做用於調用的對象修飾靜態方法:
package cn.com.dotleo.sync; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by liufei on 2018/6/24. */ public class Sync3 { public static synchronized void test3(int x) { for (int i = 0; i < 10; i++) { System.out.println("["+ x + "]test3: " + i); } } public static void main(String[] args) { final Sync3 sync3 = new Sync3(); final Sync3 syncNew = new Sync3(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { public void run() { sync3.test3(1); } }); executorService.execute(new Runnable() { public void run() { syncNew.test3(2); } }); } }
代碼參見Sync3.java
關於synchronized修飾靜態方法和類再也不演示兩個線程調用同一個對象,由於那樣確定是加鎖的順序輸出。
[1]test3: 0 [1]test3: 1 [1]test3: 2 [1]test3: 3 [1]test3: 4 [1]test3: 5 [1]test3: 6 [1]test3: 7 [1]test3: 8 [1]test3: 9 [2]test3: 0 [2]test3: 1 [2]test3: 2 [2]test3: 3 [2]test3: 4 [2]test3: 5 [2]test3: 6 [2]test3: 7 [2]test3: 8 [2]test3: 9
修飾靜態類:
package cn.com.dotleo.sync; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by liufei on 2018/6/24. */ public class Sync4 { public static void test4(int x) { synchronized (Sync4.class) { for (int i = 0; i < 10; i++) { System.out.println("["+ x + "]test4: " + i); } } } public static void main(String[] args) { final Sync4 sync4 = new Sync4(); final Sync4 syncNew = new Sync4(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { public void run() { sync4.test4(1); } }); executorService.execute(new Runnable() { public void run() { syncNew.test4(2); } }); } }
代碼參見Sync4.java
[1]test4: 0 [1]test4: 1 [1]test4: 2 [1]test4: 3 [1]test4: 4 [1]test4: 5 [1]test4: 6 [1]test4: 7 [1]test4: 8 [1]test4: 9 [2]test4: 0 [2]test4: 1 [2]test4: 2 [2]test4: 3 [2]test4: 4 [2]test4: 5 [2]test4: 6 [2]test4: 7 [2]test4: 8 [2]test4: 9
從上面的兩個示例中均可以看出,在兩個線程調用一個類的不一樣對象時,仍然能保證原子性。
因而可知:
synchronized
修飾的靜態方法,做用於這個類的全部對象synchronized
修飾的類,做用於這個類的全部對象特別強調:
synchronized
不是方法申明的一部分,若是子類繼承了父類的synchronized
方法,除非子類不會有synchronized
修飾的。