摘要:尤其關鍵的是,當一個線程訪問的一個同步代碼塊或同步方法時,其他線程對中所有其它同步代碼塊或同步方法的訪問將被阻塞。同步代碼塊是對一個對象作為參數進行鎖定。
為什么需要同步多線程?
線程的同步是指讓多個運行的線程在一起良好地協作,達到讓多線程按要求合理地占用釋放資源。我們采用Java中的同步代碼塊和同步方法達到這樣的目的。比如這樣的解決多線程無固定序執行的問題:
public class TwoThreadTest { public static void main(String[] args) { Thread th1= new MyThread1(); Thread th2= new MyThread2(); th1.start(); th2.start(); } } class MyThread2 extends Thread{ @Override public void run() { for( int i=0;i<10;i++) System. out.println( "thread 1 counter:"+i); } } class MyThread1 extends Thread{ @Override public void run() { for( int i=0;i<10;i++) System. out.println( "thread 2 counter:"+i); } }
這種狀態下多線程執行的結果是隨機地去任意插入執行,這完全取決于JVM對于線程的調度,在很多要求定序執行的情況下,這種隨機執行的狀態顯然是不合要求的。
public class ThreadTest { public static void main(String[] args) { MyThread thread = new MyThread(); Thread th1= new Thread(thread); Thread th2= new Thread(thread); th1.start(); th2.start(); } } class MyThread implements Runnable{ @Override public synchronized void run() { for( int i=0;i<10;i++) System. out.println(Thread. currentThread().getName()+" counter:"+i); } }
使用了同步方法后我們就可以控制線程獨占執行體對象,這樣在執行的過程中就可以使得線程將執行體上的任務一次性執行完后退出鎖定狀態,JVM再調度另一個線程進來一次性運行執行體內的任務。
線程創建運行的范式
在以前我們也有自己的線程創建和運行的編程范式,一般是定義一個執行類重寫run()方法,但是這種方式將執行體和執行的任務放在了一起,從軟件工程的角度來看不利于解耦。一個線程的執行的意思是說線程通過執行對象執行了某個對象的某個任務,從這個角度來說,將任務的規定者從執行類中分離出來可以使得多線程編程的各個角色明晰出來,進而獲得良好地解耦,以下就是線程創建和執行的編程范式:
public class FormalThreadClass { public static void main(String[] args) { Thread thread = new Thread( new MyRunnable()); thread.start(); } } class MyRunnable implements Runnable{ MyTask myTask = new MyTask(); @Override public void run() { myTask.doTask(); } } class MyTask{ public void doTask() { System. out.println( "This is real Tasking"); } }
synchronized關鍵字
synchronized可以用來修飾方法以構成同步方法,還可以修飾對象構成同步代碼塊,最終的目的都是一樣的:
給要訪問數據的線程添加一個規定:一次只允許一個線程訪問數據。只有?當前正在訪問數據”的線程結束訪問之后,其他線程才允許訪問這個數據。
關于synchronized關鍵字,有以下幾點來說明:
當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多只有一個線程執行該段代碼。
當兩個并發線程訪問同一個對象object中的這個synchronized同步代碼塊或同步方法時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊或同步方法以后才能執行該代碼塊或同步方法。
然而,當一個線程訪問object的一個synchronized同步代碼塊或同步方法時,另一個線程仍然可以訪問該object中的非synchronized同步代碼塊或非synchronized同步方法。
尤其關鍵的是,當一個線程訪問object的一個synchronized同步代碼塊或同步方法時,其他線程對object中所有其它synchronized同步代碼塊或同步方法的訪問將被阻塞。
1.以下這個例子可以說明synchronized方法的這些特性,同步代碼塊也是一樣:
① synchronized方法表面上它只是鎖定了當前的方法本身,實際上當synchronized方法起作用的時候,整個對象的帶有synchronized的方法都將被鎖定,這也就是為什么當一個線程執行一個synchronized方法時,其他的線程除了不能訪問當前的同步方法外還并不能訪問其他的同步方法,而只能訪問非synchronized方法,因為這種鎖定是對象級別的。
public class ThreadTest { public static void main(String[] args) { final MyTask myTask = new MyTask(); Thread thread1 = new Thread( new Runnable() { public void run() { myTask.doTask1(); } }); Thread thread2 = new Thread( new Runnable() { public void run() { myTask.doTask2(); } }); thread1.start(); thread2.start(); } } class MyTask{ public synchronized void doTask1() { for ( int i = 0; i < 5; i++) { System. out.println( "1 This is real Tasking "+i); } } public void doTask2() { for ( int i = 0; i < 5; i++) { System. out.println( "2 This is real Tasking "+i); } } }
② 如使在靜態方法中用synchronized時,因為這個方法就不是僅屬于某個對象而是屬于整個類的了,所以一旦一個線程進入了這個代碼塊就會將這個類的所有對象的所有synchronized方法或synchronized同步代碼塊鎖定,其他的線程就沒有辦法訪問所有這些對象的synchronized方法和synchronized代碼塊(注意其他線程還是仍然能訪問這些對象的非synchronized方法和synchronized代碼塊的),因此這種鎖定是class級別的。
public class FormalThreadClass { public static void main(String[] args) { MyTask myTask1 = new MyTask(); MyTask myTask2 = new MyTask(); Thread thread1 = new Thread( new MyRunnable(myTask1)); Thread thread2 = new Thread( new MyRunnable(myTask2)); thread1.start(); thread2.start(); } } class MyRunnable implements Runnable { MyTask myTask; public MyRunnable(MyTask myTask) { this. myTask = myTask; } @Override public void run() { MyTask. doTask(); } } class MyTask { public static synchronized void doTask() { for ( int i = 0; i < 5; i++) { System. out.println(Thread. currentThread().getName()+" running "+i); } } }
2.synchronized同步代碼塊是對一個對象作為參數進行鎖定。
① 如在使用synchronized(this)時,一旦一個線程進入了這個代碼塊就會將整個對象的所有synchronized方法或synchronized同步代碼塊鎖定,其他的線程就沒有辦法訪問這個對象的synchronized方法和synchronized代碼塊(注意其他線程還是仍然能訪問這個對象的非synchronized方法和synchronized代碼塊的)。
public class ThreadTest { public static void main(String[] args) { final MyTask myTask = new MyTask(); Thread thread1 = new Thread( new Runnable() { public void run() { myTask.doTask1(); } }); Thread thread2 = new Thread( new Runnable() { public void run() { myTask.doTask2(); } }); thread1.start(); thread2.start(); } } class MyTask { public void doTask1() { synchronized (this) { for ( int i = 0; i < 5; i++) { System. out.println( "1 is running"); } } } public void doTask2() { for ( int i = 0; i < 5; i++) { System. out.println( "2 is running"); } } }
所以:synchronized方法實際上等同于用一個synchronized塊包住方法中的所有語句,然后在synchronized塊的括號中傳入this關鍵字。當然,如果是靜態方法,需要鎖定的則是class對象。
① 如在使用synchronized(.class)時,一旦一個線程進入了這個代碼塊就會將整個類的所有這個synchronized(.class) 同步代碼塊鎖定,其他的線程就沒有辦法訪問這個對象的synchronized(**.class) 代碼塊,這種鎖也是class級別的,但要注意在這種情況下,其他線程仍然是可以訪問僅做了synchronized的代碼塊或非靜態方法的,因為它們僅僅是對當前對象的鎖定。
public class FormalThreadClass { public static void main(String[] args) { MyTask myTask1 = new MyTask(); MyTask myTask2 = new MyTask(); Thread thread1 = new Thread( new MyRunnable(myTask1)); Thread thread2 = new Thread( new MyRunnable(myTask2)); thread1.start(); thread2.start(); } } class MyRunnable implements Runnable { MyTask myTask; public MyRunnable(MyTask myTask) { this. myTask = myTask; } @Override public void run() { myTask.doTask(); } } class MyTask { public void doTask() { synchronized (MyTask.class ) { for ( int i = 0; i < 5; i++) { System. out.println(Thread. currentThread().getName()+" running "+i); } } } }
總結起來這一部分:
synchronized方法是一種粗粒度的并發控制手段,某一時刻只能有一個線程執行該方法。synchroized塊則是一種細粒度的并發控制,只會將塊中的代碼同步,位于方法內synchroized塊之外的代碼是可以被多個線程同時訪問到。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64615.html
摘要:本文對多線程基礎知識進行梳理,主要包括多線程的基本使用,對象及變量的并發訪問,線程間通信,的使用,定時器,單例模式,以及線程狀態與線程組。源碼采用構建,多線程這部分源碼位于模塊中。通知可能等待該對象的對象鎖的其他線程。 本文對多線程基礎知識進行梳理,主要包括多線程的基本使用,對象及變量的并發訪問,線程間通信,lock的使用,定時器,單例模式,以及線程狀態與線程組。 寫在前面 花了一周時...
摘要:關鍵字加到非靜態方法上持有的是對象鎖。線程和線程持有的鎖不一樣,所以和運行同步,但是和運行不同步。所以盡量不要使用而使用參考多線程編程核心技術并發編程的藝術如果你覺得博主的文章不錯,歡迎轉發點贊。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)synchronized關鍵字(2) J...
摘要:轉載請備注地址多線程學習二將分為兩篇文章介紹同步方法另一篇介紹同步語句塊。如果兩個線程同時操作對象中的實例變量,則會出現非線程安全,解決辦法就是在方法前加上關鍵字即可。 轉載請備注地址: https://blog.csdn.net/qq_3433... Java多線程學習(二)將分為兩篇文章介紹synchronized同步方法另一篇介紹synchronized同步語句塊。系列文章傳送門...
摘要:在兩個線程訪問同一個對象中的同步方法時一定是線程安全的。當一個線程訪問的一個同步代碼塊時,其他線程對同一個鐘所有其他同步代碼塊的訪問被阻塞,這說明使用的對象監視器是一個。 非線程安全其實會在多個線程對同一個對象中的實例變量進行并發訪問時發生,產生的后果就是臟讀,也就是取到的數據其實是被更改過的。而線程安全就是以獲得的實例變量的值是經過同步處理的,不會出現臟讀的現象。 非線程安全問題存...
摘要:無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執行單元獲得鎖。另外在中引入了自適應的自旋鎖。和關鍵字的總結推薦一 該文已加入開源文檔:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識)。地址:https://github.com/Snailclimb... 本文是對 synchronized 關鍵字使用、底層原理、JD...
閱讀 1181·2023-04-26 02:42
閱讀 1633·2021-11-12 10:36
閱讀 1780·2021-10-25 09:47
閱讀 1262·2021-08-18 10:22
閱讀 1801·2019-08-30 15:52
閱讀 1213·2019-08-30 10:54
閱讀 2635·2019-08-29 18:46
閱讀 3496·2019-08-26 18:27