Java線程中關于Synchronized的用法

synchronized是Java中的關鍵字,是一種同步鎖。它修飾的對象有以下幾種:?
1. 修飾一個代碼塊,被修飾的代碼塊稱為同步語句塊,其作用的范圍是大括號{}括起來的代碼,作用的對象是調用這個代碼塊的對象;?
2. 修飾一個方法,被修飾的方法稱為同步方法,其作用的范圍是整個方法,作用的對象是調用這個方法的對象;?
3. 修改一個靜態的方法,其作用的范圍是整個靜態方法,作用的對象是這個類的所有對象;?

4. 修改一個,其作用的范圍是synchronized后面括號括起來的部分,作用主的對象是這個類的所有對象。

-----------------------------------------------------------------------------------------------------------

注:(引用自JDK1.9)

我們先來了解一下Thread的其中一個構造函數,這個構造函數有兩個參數:分別是Runnable,另一個是String。

Runnable表示:當這個線程啟動時,它的run方法被調用。如果null,則調用該螺紋的run方法。

String表示:新線程的名稱。



--------------------------------------------------------------------------------------------------

1、修飾一個代碼塊

  1. 一個線程訪問一個對象中的synchronized(this)同步代碼塊時,其他試圖訪問該對象的線程將被阻塞。

【Demo1】:synchronized的用法

舉個栗子:

/*** 同步線程*/public class Test{public static void main(String[] args) {SyncThread syncThread = new SyncThread();Thread thread1 = new Thread(syncThread, "SyncThread1");//ConstructorThread thread2 = new Thread(syncThread, "SyncThread2");//Constructorthread1.start();thread2.start();}static class SyncThread implements Runnable {private static int count;public SyncThread() {count = 0;}public  void run() {synchronized(this) {//修飾代碼塊for (int i = 0; i < 5; i++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}public int getCount() {return count;}}
}

運行結果:
????

當兩個并發線程(thread1和thread2)訪問同一個對象(syncThread)中的synchronized代碼塊時,在同一時刻只能有一個線程得到執行,另一個線程受阻塞,必須等待當前線程執行完這個代碼塊以后才能執行該代碼塊。Thread1和thread2是互斥的,因為在執行synchronized代碼塊時會鎖定當前的對象,只有執行完該代碼塊才能釋放該對象鎖,下一個線程才能執行并鎖定該對象。?

下面我們來修改一下代碼:

/*** 同步線程*/public class Test{public static void main(String[] args) {/*SyncThread syncThread = new SyncThread();Thread thread1 = new Thread(syncThread, "SyncThread1");//ConstructorThread thread2 = new Thread(syncThread, "SyncThread2");//Constructorthread1.start();thread2.start();*/SyncThread syncThread1 = new SyncThread();SyncThread syncThread2 = new SyncThread();Thread thread1 = new Thread(syncThread1, "SyncThread1");//ConstructorThread thread2 = new Thread(syncThread2, "SyncThread2");thread1.start();thread2.start();}static class SyncThread implements Runnable {private static int count;public SyncThread() {count = 0;}public  void run() {synchronized(this) {for (int i = 0; i < 5; i++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}public int getCount() {return count;}}
}

運行之后:


我們發現syncThread1和syncThread2交替執行:

????????這時創建了兩個SyncThread的對象syncThread1和syncThread2,線程thread1執行的是syncThread1對象中的synchronized代碼(run),而線程thread2執行的是syncThread2對象中的synchronized代碼(run);我們知道synchronized鎖定的是對象,這時會有兩把鎖分別鎖定syncThread1對象和syncThread2對象,而這兩把鎖是互不干擾的,不形成互斥,所以兩個線程可以同時執行。

2.當一個線程訪問對象的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該對象中的非synchronized(this)同步代碼塊。?
【Demo2】:多個線程訪問synchronized和非synchronized代碼塊

舉個栗子:

public class Test{public static void main(String[] args) {Counter counter = new Counter();Thread thread1 = new Thread(counter, "A");Thread thread2 = new Thread(counter, "B");thread1.start();thread2.start();}static class Counter implements Runnable{private int count;public Counter() {count = 0;}public void countAdd() {synchronized(this) {for (int i = 0; i < 5; i ++) {try {System.out.println(Thread.currentThread().getName() +"->"+"Synchronized:" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}//非synchronized代碼塊,未對count進行讀寫操作,所以可以不用synchronizedpublic void printCount() {for (int i = 0; i < 5; i ++) {try {System.out.println(Thread.currentThread().getName() +"->"+ "NoSynchronized:" + count);Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public void run() {String threadName = Thread.currentThread().getName();if (threadName.equals("A")) {countAdd();} else if (threadName.equals("B")) {printCount();}}}
}

運行結果,如下:


上面代碼中countAdd是一個synchronized的,printCount是非synchronized的。從上面的結果中可以看出一個線程訪問一個對象的synchronized代碼塊時,別的線程可以訪問該對象的非synchronized代碼塊而不受阻塞。

3.指定要給某個對象加鎖

【Demo3】:指定要給某個對象加鎖

舉個栗子:

public class Test{public static void main(String[] args) {Account account = new Account("zhang san", 10000.0f);AccountOperator accountOperator = new AccountOperator(account);final int THREAD_NUM = 5;Thread threads[] = new Thread[THREAD_NUM];for (int i = 0; i < THREAD_NUM; i ++) {threads[i] = new Thread(accountOperator, "Thread" + i);threads[i].start();}		}//static class Account {String name;float amount;public Account(String name, float amount) {this.name = name;this.amount = amount;}//存錢public  void deposit(float amt) {amount += amt;try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}//取錢public  void withdraw(float amt) {amount -= amt;try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}public float getBalance() {return amount;}}//static class AccountOperator implements Runnable{private Account account;public AccountOperator(Account account) {this.account = account;}public void run() {synchronized (account) {account.deposit(500);account.withdraw(500);System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());}}}
}

運行結果:


在AccountOperator 類中的run方法里,我們用synchronized 給account對象加了鎖。這時,當一個線程訪問account對象時,其他試圖訪問account對象的線程將會阻塞,直到該線程訪問account對象結束。也就是說誰拿到那個鎖誰就可以運行它所控制的那段代碼。?

當有一個明確的對象作為鎖時,就可以用類似下面這樣的方式寫程序。

public void method3(SomeObject obj)
{//obj 鎖定的對象synchronized(obj){// todo}
}

沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創建一個特殊的對象來充當鎖:

class Test implements Runnable
{private byte[] lock = new byte[0];  // 特殊的instance變量public void method(){synchronized(lock) {// todo 同步代碼塊}}public void run() {}
}

說明:零長度的byte數組對象創建起來將比任何對象都經濟――查看編譯后的字節碼:生成零長度的byte[]對象只需3條操作碼,而Object lock = new Object()則需要7行操作碼。

------------------------------------詳細理解--------

2、修飾一個方法

Synchronized修飾一個方法很簡單,就是在方法的前面加synchronized,public synchronized void method(){//todo}; synchronized修飾方法和修飾一個代碼塊類似,只是作用范圍不一樣,修飾代碼塊是大括號括起來的范圍,而修飾方法范圍是整個函數。如將【Demo1】中的run方法改成如下的方式,實現的效果一樣。

*【Demo4】:synchronized修飾一個方法

public synchronized void run() {for (int i = 0; i < 5; i ++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}
}

Synchronized作用于整個方法的寫法。?
寫法一:

public synchronized void method()
{// todo
}

寫法二:

public void method()
{synchronized(this) {// todo}
}

寫法一修飾的是一個方法,寫法二修飾的是一個代碼塊,但寫法一與寫法二是等價的,都是鎖定了整個方法時的內容。

在用synchronized修飾方法時要注意以下幾點:?
1. synchronized關鍵字不能繼承。?
雖然可以使用synchronized來定義方法,但synchronized并不屬于方法定義的一部分,因此,synchronized關鍵字不能被繼承。如果在父類中的某個方法使用了synchronized關鍵字,而在子類中覆蓋了這個方法,在子類中的這個方法默認情況下并不是同步的,而必須顯式地在子類的這個方法中加上synchronized關鍵字才可以。當然,還可以在子類方法中調用父類中相應的方法,這樣雖然子類中的方法不是同步的,但子類調用了父類的同步方法,因此,子類的方法也就相當于同步了。這兩種方式的例子代碼如下:?
在子類方法中加上synchronized關鍵字

class Parent {public synchronized void method() { }
}
class Child extends Parent {public synchronized void method() { }
}

在子類方法中調用父類的同步方法

class Parent {public synchronized void method() {   }
}
class Child extends Parent {public void method() { super.method();   }
} 
  1. 在定義接口方法時不能使用synchronized關鍵字。
  2. 構造方法不能使用synchronized關鍵字,但可以使用synchronized代碼塊來進行同步。?


修飾一個靜態的方法

Synchronized也可修飾一個靜態方法,用法如下:

public synchronized static void method() {// todo
}

我們知道靜態方法是屬于類的而不屬于對象的。同樣的,synchronized修飾的靜態方法鎖定的是這個類的所有對象。我們對Demo1進行一些修改如下:

【Demo5】:synchronized修飾靜態方法

/*** 同步線程*/
class SyncThread implements Runnable {private static int count;public SyncThread() {count = 0;}public synchronized static void method() {for (int i = 0; i < 5; i ++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void run() {method();}
}

調用代碼:

SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();

結果如下:

SyncThread1:0?
SyncThread1:1?
SyncThread1:2?
SyncThread1:3?
SyncThread1:4?
SyncThread2:5?
SyncThread2:6?
SyncThread2:7?
SyncThread2:8?
SyncThread2:9

syncThread1和syncThread2是SyncThread的兩個對象,但在thread1和thread2并發執行時卻保持了線程同步。這是因為run中調用了靜態方法method,而靜態方法是屬于類的,所以syncThread1和syncThread2相當于用了同一把鎖。這與Demo1是不同的。



修飾一個類

Synchronized還可作用于一個類,用法如下:

class ClassName {public void method() {synchronized(ClassName.class) {// todo}}
}

我們把Demo5再作一些修改。?
【Demo6】:修飾一個類

/*** 同步線程*/
class SyncThread implements Runnable {private static int count;public SyncThread() {count = 0;}public static void method() {synchronized(SyncThread.class) {for (int i = 0; i < 5; i ++) {try {System.out.println(Thread.currentThread().getName() + ":" + (count++));Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}public synchronized void run() {method();}
}

其效果和【Demo5】是一樣的,synchronized作用于一個類T時,是給這個類T加鎖,T的所有對象用的是同一把鎖。



總結:

A. 無論synchronized關鍵字加在方法上還是對象上,如果它作用的對象是非靜態的,則它取得的鎖是對象;如果synchronized作用的對象是一個靜態方法或一個類,則它取得的鎖是對類,該類所有的對象同一把鎖。?
B. 每個對象只有一個鎖(lock)與之相關聯,誰拿到這個鎖誰就可以運行它所控制的那段代碼。?
C. 實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。


參考資料:?

http://blog.csdn.net/chenguang79/article/details/677720?

http://developer.51cto.com/art/200906/132354.htm

https://blog.csdn.net/luoweifu/article/details/46613015











本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274025.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274025.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274025.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

cmd命令行設置環境變量

http://blog.sciencenet.cn/blog-51026-566742.html 1、查看當前所有可用的環境變量&#xff1a;輸入 set 即可查看。 2、查看某個環境變量&#xff1a;輸入 “set 變量名”即可&#xff0c;比如想查看path變量的值&#xff0c;即輸入 set path。 3、修改環境變量 &#xff1a;…

Java線程之多線程與多進程(1)——以操作系統的角度述說線程與進程

任務調度 線程是什么&#xff1f;要理解這個概念&#xff0c;須要先了解一下操作系統的一些相關概念。大部分操作系統(如Windows、Linux)的任務調度是采用時間片輪轉的搶占式調度方式&#xff0c;也就是說一個任務執行一小段時間后強制暫停去執行下一個任務&#xff0c;每個任務…

用 PS 調整服務器時間

用 PS 調整服務器時間 Powershell 有一個命令用來調整計算機的時間&#xff0c; Set-Date -Adjust&#xff0c;但是&#xff0c;只能調整本地時間&#xff0c;不能調整遠程的計算機時間。 function AdjustDCTime ( $Server, $addTime ){ $Svr Get-WmiObject Win32_Operatin…

Java線程之多線程與多進程(2)——線程優先級與線程安全

線程優先級 現在主流操作系統(如Windows、Linux、Mac OS X)的任務調度除了具有前面提到的時間片輪轉的特點外&#xff0c;還有優先級調度(Priority Schedule)的特點。優先級調度決定了線程按照什么順序輪流執行&#xff0c;在具有優先級調度的系統中&#xff0c;線程擁有各自的…

mahout貝葉斯算法開發思路(拓展篇)1

首先說明一點&#xff0c;此篇blog解決的問題是就下面的數據如何應用mahout中的貝葉斯算法&#xff1f;&#xff08;這個問題是在上篇&#xff08;。。。完結篇&#xff09;blog最后留的問題&#xff0c;如果想直接使用該工具&#xff0c;可以在mahout貝葉斯算法拓展下載&#…

Java線程之多線程與多進程(3)——Java中的多線程

單線程 任何程序至少有一個線程&#xff0c;即使你沒有主動地創建線程&#xff0c;程序從一開始執行就有一個默認的線程&#xff0c;被稱為主線程&#xff0c;只有一個線程的程序稱為單線程程序。如下面這一簡單的代碼&#xff0c;沒有顯示地創建一個線程&#xff0c;程序從mai…

幾種常用控件的使用方法

1.UIActivityIndicatorView的使用 UIActivityIndicatorView *activity[[[UIActivityIndicatorViewalloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]autorelease]; z [activity setFrame:CGRectMake(150,150, 50, 50)]; [self.window addSubview:activ…

Java-正則表達式

什么是正則表達式&#xff1f; 正則表達式(Regular Expression)就是用某種模式去匹配一類字符串的公式。如你要在一篇文章中查找第一個字是“李”最后一個字是“建”的三個字的姓名&#xff0c;即“李*建”&#xff1b;那么“李*建”就是公式&#xff0c;也稱作模式(Pattern)&a…

tab標簽的另一種寫法

<div class"good"><ul><li><span>歌曲精選</span></li><li class"other"><span>MV精選</span></li></ul><div class"music_good"><p><span>丁當</span…

java 中 if與while的區別

if&#xff1a;就是一個判斷的&#xff0c;如果滿足后面的條件就繼續運行if語句里面的東西的&#xff0c;要是不滿足就跳出來&#xff0c;執行else語句或執行下面的語句的 。while&#xff1a;就是循環語句的&#xff0c;當滿足while里面的條件時&#xff0c;就會執行里面的循環…

install yael on the ubuntu 12.04

1. bits/predefs.h no such file or directory ??? sudo apt-get install gcc-multilib 2. sudo gedit /etc/profile PATH$PATH:/usr/local/MATLAB/R2012a/bin source /etc/profile 3.ubuntu 切換gcc 版本 1&#xff09;sudo apt-get install gcc-4.4 g-4.4 g-4.4-multi…

Java 線程多線程編程3---線程同步之生產者與消費者問題

生產者與消費者問題&#xff1a; 第一步&#xff1a;把架子搭起來 package com.zhj.www;public class ProceduerConsumer {public static void main(String[] args) {} }//饅頭實體 class wotou{int id;wotou(int id) {this.id id;}public String toString() {return "wo…

windows 服務實例

參考來源:http://blog.csdn.net/morewindows/article/details/6858216 參考來源: http://hi.baidu.com/tfantasy/item/aefa43d66b470a2b38f6f76c 剩下的都是我自己整理的。 在VS2012中新建一個Windows 服務的項目。然后在解決方案目錄下找到Services1.cs&#xff0c;切換到代碼…

Java 線程多線程編程2---線程同步

來模擬一個死鎖&#xff08;互相等待&#xff09;&#xff1a; TestDeadLock.java package com.zhj.www;public class TestDeadLock implements Runnable {public int flag 1;static Object o1 new Object();static Object o2 new Object();public void run() {System.out.p…

Java網絡編程1---基礎

TCP/IP:事實上的標準 自己編的應用程序&#xff1a;應用層 TCP/UDP層 IP層 物理層 數據封裝&#xff1a;第五層只與第四層打交道。 數據拆封《TCP/IP詳解》網絡底層 IP巨大的貢獻&#xff1a;提供了獨一無二的IP地址。 內網IP&#xff1a;虛假的 子網掩碼&#xff1a;255.255.2…

Java網絡編程2---Socket-TCP編程

Sockct:插座Socket是關于TCP的。 端口號&#xff1a;兩個字節->65536個端口號&#xff0c;一個應用程序占多個端口號&#xff1b; 但是假設一個應用程序占一個端口號&#xff1b;一臺電腦會有65535個應用程序。 自己編寫程序要占用端口號1024以上后的。 80端口&#xff1a;網…

winform綁定多張圖片

開發winform程序的時候經常設計到要顯示多張圖片的問題&#xff0c;其解決思路一般是先遍歷文件夾中的所有圖片&#xff0c;然后再把這些圖片添加到ImageList控件中&#xff0c;最后再綁定顯示出來。這里我們介紹兩種綁定的方法&#xff1a; &#xff08;一&#xff09;動態生成…

Java網絡編程3---Socket-UDP編程

栗子&#xff1a;TestUDPServer.java 服務器端&#xff1a; package com.zhj.www;import java.net.DatagramPacket; import java.net.DatagramSocket;public class TestUDPServer {public static void main(String[] args)throws Exception {byte buf[] new byte[1024];Datagr…

iOS 6 自動布局入門

http://www.raywenderlich.com/zh-hans/22873/ios-6-自動布局-入門&#xff0d;1轉載于:https://www.cnblogs.com/ihojin/p/auto-layout.html

Java GUI 基礎知識

這部分主要包含AWT、組件和容器、布局管理器Component&#xff1a;所有可以和用戶交互的圖形元素&#xff0c;他的子類有&#xff1a;輸入框… Java.awt及其子包 Container&#xff1a;容器&#xff0c;容納其他各種各樣的Component的元素。 Panel&#xff1a;可以容納其他元素…