相信你們都據說過線程安全問題,在學習操做系統的時候有一個知識點是臨界資源,簡單的說就是一次只能讓一個進程操做的資源,可是咱們在使用多線程的時候是併發操做的,並不能控制同時只對一個資源的訪問和修改,想要控制那麼有幾種操做,今天咱們就來說講第一種方法:線程同步塊或者線程同步方法(synchronized)html
- 下面舉一個例子說明
synchronized
關鍵字的使用
public class Sychor { public void insert(Thread thread) { for (int i = 0; i < 10; i++) { System.out.println(thread.getName() + "輸出: " + i); } } public static void main(String[] args) { final Sychor sychor = new Sychor(); Thread t1 = new Thread() { public void run() { sychor.insert(Thread.currentThread()); }; }; Thread t2 = new Thread() { public void run() { sychor.insert(Thread.currentThread()); }; }; t1.start(); t2.start(); } }
其中輸出結果爲下圖java
從上面的結果能夠看出這裏的兩個線程是同時執行
insert()
方法的,下面咱們在原有的代碼上添加synchronized
關鍵字看看效果如何,代碼以下:安全
public class Sychor { public synchronized void insert(Thread thread) { for (int i = 0; i < 10; i++) { System.out.println(thread.getName() + "輸出: " + i); } } public static void main(String[] args) { final Sychor sychor = new Sychor(); Thread t1 = new Thread() { public void run() { sychor.insert(Thread.currentThread()); }; }; Thread t2 = new Thread() { public void run() { sychor.insert(Thread.currentThread()); }; }; t1.start(); t2.start(); } }
上面程序的運行結果我就不列出來,本身能夠試試,總之就是加上了
synchronized
關鍵字使得線程是一個一個的執行的,只有先執行完一個線程才能執行了另一個線程。多線程
固然上面的咱們使用的是線程同步方法,咱們可使用線程同步塊,這兩個相比線程同步塊更加靈活,只須要將須要同步的代碼放在同步塊中便可,代碼以下;併發
public class Sychor { public void insert(Thread thread) { synchronized (this) { for (int i = 0; i < 10; i++) { System.out.println(thread.getName() + "輸出: " + i); } } } public static void main(String[] args) { final Sychor sychor = new Sychor(); Thread t1 = new Thread() { public void run() { sychor.insert(Thread.currentThread()); }; }; Thread t2 = new Thread() { public void run() { sychor.insert(Thread.currentThread()); }; }; t1.start(); t2.start(); } }
從上面的代碼中能夠看出這種方式更加靈活,只須要將須要同步的代碼方法在同步塊中,不須要同步的代碼放在外面jvm
- 咱們知道每個對象都有一把鎖,當咱們使用線程同步方法或者線程同步塊的時候實際上得到是對象的惟一的一把鎖,當一個線程得到了這惟一的鎖,那麼其餘的線程只能拒之門外了,注意這裏咱們說是一個對象,也就是說是同一個對象,若是是不一樣的對象,那麼就不起做用了,由於不一樣對象有不一樣的對象鎖,好比咱們將上面的程序改爲以下:
public class Sychor { public void insert(Thread thread) { synchronized (this) { for (int i = 0; i < 10; i++) { System.out.println(thread.getName() + "輸出: " + i); } } } public static void main(String[] args) { //第一個線程 Thread t1 = new Thread() { public void run() { Sychor sychor = new Sychor(); //在run() 方法中建立一個對象 sychor.insert(Thread.currentThread()); }; }; //第二個線程 Thread t2 = new Thread() { public void run() { Sychor sychor = new Sychor(); //建立另外的一個對象 sychor.insert(Thread.currentThread()); }; }; t1.start(); t2.start(); } }
從上面的結果可知,此時線程同步塊根本不起做用,由於他們調用的是不一樣對象的insert方法,得到鎖是不同的學習
- 上面咱們已經說過一個對象有一把鎖,線程同步方法和線程同步塊實際得到的是對象的鎖,所以線程同步塊的括號中填入的是
this
,咱們都知道this
在一個類中的含義
- 一個類也有惟一的一把鎖,咱們前面說的是使用對象調用成員方法,如今若是咱們要調用類中的靜態方法,那麼咱們可使用線程同步方法或者同步塊得到類中的惟一一把鎖,那麼對於多個線程同時調用同一個類中的靜態方法就能夠實現控制了,代碼以下:
public class Sychor { // 靜態方法 public static synchronized void insert(Thread thread) { for(int i=0;i<10;i++) { System.out.println(thread.getName()+"輸出 "+i); } } public static void main(String[] args) { //第一個線程 Thread t1 = new Thread() { public void run() { Sychor.insert(Thread.currentThread()); //直接使用類調用靜態方法 }; }; //第二個線程 Thread t2 = new Thread() { public void run() { Sychor.insert(Thread.currentThread()); //直接使用類調用靜態方法 }; }; t1.start(); t2.start(); } }
- 要想實現線程安全和同步控制,若是執行的是非
static
同步方法或者其中的同步塊,那麼必定要使用同一個對象,若是調用的是static同步方法或者其中的同步塊那麼必定要使用同一個類去調用
- 若是一個線程訪問的是
static
同步方法,而另一個線程訪問的是非static的同步方法,此時這兩個是不會發生衝突的,由於一個是類的鎖,一個是對象的鎖
- 若是使用線程同步塊,那麼同步塊中的代碼是控制訪問的,可是外面的代碼是全部線程均可以訪問的
- 當一個正在執行同步代碼塊的線程出現了異常,那麼
jvm
會自動釋放當前線程所佔用的鎖,所以不會出現因爲異常致使死鎖的現象