synchronized
是一種同步鎖。他修飾的對象有一下幾種:
- 修飾一個代碼塊,被修飾的代碼塊稱為同步語句塊演训,其作用的范圍是大括號括起來的代碼弟孟,作用的對象是調(diào)用這個代碼塊的對象。
- 修飾一個方法仇祭,被修飾的方法稱為同步語句塊披蕉,其作用的范圍是整個方法,作用的對象是調(diào)用這個方法的對象乌奇。
- 修飾一個靜態(tài)的方法,其作用是整個靜態(tài)方法眯娱,作用的對象是這個類的所有對象礁苗。
- 修飾一個類,其作用的范圍是synchronized后面括號括起來的部分徙缴,主作用的對象是這個類的所有對象试伙。
修飾一個代碼塊
- 一個線程訪問一個對象中的同步代碼塊時:
class SyncThread impltements Runnable{
private static final count;
public SyncThread(){
count = 0;
}
public void run(){
synchronized(this){
for(int i = 0;i<5;i++){
try{
System.out.println(Thread.currentThead().getName()+":"+(count++))
Thread.sleep(100);
}catch(....){
.......
}
}
}
}
public int getCount(){
return count;
}
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "11");
Thread thread2 = new Thread(syncThread, "22");
thread1.start();
thread2.start();
}
運(yùn)行結(jié)果:
11:0
11:1
11:2
11:3
11:4
22:5
22:6
22:7
22:8
22:9
當(dāng)兩個并發(fā)線程同時訪問同一個對象中的同步代碼塊時嘁信,在同一時刻只能有一個線程得到執(zhí)行,另一個線程受到阻塞疏叨,必須等待當(dāng)前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊潘靖。
但是當(dāng)我們把運(yùn)行代碼改一下:
Thread thread1 = new Thread(new SyncThread(),"11");
Thread thread2 = new Thread(new SyncThread(), "22");
thread1.start();
thread2.start();
運(yùn)行結(jié)果:
11:0
22:1
11:2
22:3
11:4
22:5
22:6
11:7
11:8
22:9
因?yàn)橐陨洗a中synchronized
鎖定的是對象,這時會有兩把鎖分別鎖定對象1和對象2蚤蔓,而這兩把鎖時互不干擾的卦溢,不形成互斥,所以兩個線程可以同時執(zhí)行秀又。
2.當(dāng)一個線程訪問對象的一個同步代碼塊時单寂,另一個線程仍然可以同時訪問該對象中的非synchronized
同步代碼塊。3.指定要給某個對象加鎖
class Account{
String name;
int amount;
public Account(String name, int amount){
this.name = name;
this.amount = amount;
}
//存錢
public void deposit(int amt){
amount += amt;
try{
Thread.sleep(100);
}catch(...){
....
}
}
//取錢
public void withdraw(int amt){
amount -= amt;
try{
Thread.sleep(100);
}catch(...){
...
}
}
public int getAmount(){
return amount;
}
}
class AccountOperator implements Runnable{
private Account account;
public void run(){
synchronized(account){
account.deposit(500);
account.withdraw(500);
System.out.println(Thread.currentThread().getName() + ":" + account.getAmount());
}
}
}
//調(diào)用代碼
Account account = new Account("zhangsan", 10000);
AccountOperator accountOperator = new AccountOperator(account);
final int THREAD_NUM = 5;
Thread[] threads = new Thread[THREAD_NUM];
for(int i = 0;i<THREAD_NUM;i++){
thread[i] = new Thread(accountOperator, "thread"+i);
thread[i].start();
}
運(yùn)行結(jié)果:
Thread3:10000
Thread2:10000
Thread1:10000
Thread4:10000
Thread0:10000
在AccountOperator
類的run
方法中吐辙,我們用synchronized
給account
對象加了鎖宣决。這時,當(dāng)一個線程訪問account
對象時昏苏,其他試圖訪問account
對象的線程將會阻塞尊沸,知道該線程訪問account
對象結(jié)束。
當(dāng)一個有明確的對象作為?鎖時贤惯,就可以用類似下面:
public void method3(SomeObject obj){
//obj鎖定的對象
synchronized(obj){
//todo
}
}
當(dāng)沒有明確的對象作為鎖椒丧,只是想讓一段代碼同步時,可以創(chuàng)建一個特殊的對象來充當(dāng)鎖:
class Test implements Runnable{
private byte[] lock = new byte[0];
public void method(){
synchronized(lock){
//todo
}
}
public void run(){
}
}
說明:零長度的byte
數(shù)組對象創(chuàng)建起來比任何對象都經(jīng)濟(jì):生成零長度的byte[]
對象只需3條操作碼救巷,而Object lock = new Object()
則需要7行操作碼壶熏。
修飾一個方法
public synchronized void run(){
for(int i = 0;i<5;i++){
try{
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
}catch(...){
...
}
}
}
synchronized
作用于整個方法的寫法:
public synchronized void method(){
//todo
}
或者
public void method(){
synchronized(this){
//todo
}
}
在用synchronized
修飾方法時要注意一下幾點(diǎn):
1.
synchronized
關(guān)鍵字不能繼承。
2.在定義接口方法時不能使用synchronized
關(guān)鍵字
3.構(gòu)造方法不能使用synchronized
關(guān)鍵字浦译,但可以使用sync hronized
代碼塊來進(jìn)行同步棒假。
修飾一個靜態(tài)方法
public synchronized static void method(){
//todo
}
我們知道靜態(tài)方法屬于類而不屬于對象的。同樣的精盅,synchronized
修飾的靜態(tài)方法鎖定的是這個類的所有對象帽哑。
class SyncThread implements Runnable{
private static int count;
public SyncThread(){
count = 0;
}
public synchronized void run(){
}
public synchronized static void method(){
for(int i = 0;i<5;i++){
try{
Log.e(TAG, "method: "+Thread.currentThread().getName()+":"+(count++));
Thread.sleep(100);
}catch(...){
...
}
}
}
}
調(diào)用代碼:
SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(sychThread1, "SyncThread1");
Thread thread2 = new Thread(sychThread2, "SyncThread2");
thread1.start();
thread2.start();
運(yùn)行結(jié)果:
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
并發(fā)執(zhí)行卻保持了線程同步妻枕,因?yàn)?code>run中調(diào)用了靜態(tài)方法method
,而靜態(tài)方法是屬于類的粘驰,所以syncThread1
和syncThread2
相當(dāng)于同一把鎖屡谐。
修飾一個類
class ClassName{
public void method(){
synchronized(ClassName.class){
//todo
}
}
}
class SyncThread implements Runnable{
private static int count;
public SyncThread(){
count = 0;
}
public void run(){
method();
}
public static void method(){
synchronized(SyncThread.class){
for(int i = 0; i<5;i++){
try{
log.e(TAG, "run: "+Thread.currentThread().getName()+":"+count++);
Thread.sleep(100);
}catch(...){
...
}
}
}
}
}
運(yùn)行結(jié)果:
SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9
總結(jié):1.無論synchronized
關(guān)鍵字加在方法還是對象上,如果他作用的對象是非靜態(tài)的蝌数,則它取得的鎖是對象愕掏,如果synchronized
作用的對象是一個靜態(tài)方法或一個類,則它取得的鎖是類顶伞,該類所有的對象同一把鎖饵撑。
2.每個對象只有一個鎖(lock
)與之關(guān)聯(lián)剑梳,誰拿到這個鎖誰就可以運(yùn)行它所控制的那段代碼。
3.實(shí)現(xiàn)同步時要很大的系統(tǒng)開銷作為代價的滑潘,甚至可能造成死鎖垢乙,所以盡量避免無謂的同步控制。