相信大家都聽說過線程安全問題,在學習作業系統的時候有一個知識點是臨界資源,簡單的說就是一次只能讓一個行程操作的資源,但是我們在使用多執行緒的時候是並發操作的,並不能控制同時只對一個資源的存取和修改,想要控制那麼有幾種操作,今天我們就來講講第一種方法:執行緒同步區塊或執行緒同步方法(synchronized)
下面舉個範例說明
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(); } }
##其中輸出結果為下圖
從上面的結果可以看出這裡的兩個執行緒是同時執行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(); } }
從上面的程式碼可以看出這種方式更加靈活,只需要將需要同步的程式碼方法在同步區塊中,不需要同步的程式碼放在外面詳細原因
從上面的結果可知,此時執行緒同步區塊根本不起作用,因為他們呼叫的是##我們知道
- 每個物件都有一把鎖
,當我們使用執行緒同步方法或執行緒同步區塊的時候實際上獲得是物件的唯一的一把鎖,當一個執行緒獲得了這唯一的鎖,那麼其他的線程只能拒之門外了,注意這裡我們說是一個對象,也就是說是同一個對象,如果是不同的對象,那麼就不起作用了,因為不同對像有不同的對象鎖,比如我們將上面的程式改成如下:
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
- 會自動釋放目前執行緒所佔用的鎖,因此不會出現由於異常導致死鎖的現象
#參考文章
以上是什麼是synchronized?如何使用synchronized?的詳細內容。更多資訊請關注PHP中文網其他相關文章!