先看一段**:
public class account
public void deposit(int my) catch (interruptedexception e)
this.money = tmp; }
public void withdraw(int my) catch (interruptedexception e)
this.money = tmp; }
public int getmoney()
private static final int thread_amount = 100;
private static thread threads = new thread[thread_amount];
public static void main(string args)
});threads[i].start();
} for (int i = 0; i < thread_amount; i++) catch (interruptedexception e)
} system.out.println("money is " + account.getmoney());
}}
我們用account模擬乙個賬戶。然後啟動1000個執行緒,對賬戶中的1000元進行訪問。我們先存100元,再取100元,結果應該是1000元不變。但是事與願違,我們的結果是1000、1100等隨機結果。原因是當乙個執行緒存了100元,還沒有被取走時,另乙個執行緒的結果把當前的值覆蓋掉了。這就需要我們對方法進行同步。
public synchronized void deposit(int my)
public synchronized void withdraw(int my)
這樣就能得到正確結果了,但是執行時間貌似增加了~~囧。
下面我們來介紹一下synchronized:
synchronized可以作為方法的修飾符,也可以出現在**中,如:
synchronized(o)
o是乙個object型別
1.當synchronized修飾方法的時候,就相當於類例項的該方法加了一把鎖(是每個類例項都擁有各自的鎖,而不是所有類例項共享一把鎖)。
public class threadtest
});thread t2 = new thread(new runnable()
});t1.start();
t2.start(); }}
class printclass
}}
上面的demo,這兩個執行緒都會列印,因為每個執行緒中都持有乙個例項,這兩個例項的synchronized方法鎖並不互斥。但如果改為下面這樣:
public class threadtest
});thread t2 = new thread(new runnable()
});t1.start();
t2.start(); }}
class printclass
}}
只有乙個執行緒會列印出來,因為他們持有的同乙個例項。要是synchronized修飾的是靜態方法又會怎樣呢:
public class threadtest
});thread t2 = new thread(new runnable()
});t1.start();
t2.start(); }}
class printclass
}}
還是只有乙個執行緒會列印。這是因為在靜態方法上使用synchronized,是在這個類的靜態方法上加了把鎖,同一時刻只能有乙個執行緒訪問該靜態方法。
2.當synchronized修飾**塊的時候,在誰的身上加鎖取決於synchronized(o) {}中的物件o。在這裡你可以把o看成乙個門衛,進入synchronized標註的**塊必須取得門衛的同意,門衛每次只放乙個執行緒進入。乙個門衛可以看管多個**塊,同乙個門衛只允許同乙個執行緒進入。
我們最常見到的門衛是this:
public class threadtest
});thread t2 = new thread(new runnable()
});t1.start();
t2.start(); }
}class printclass catch (interruptedexception e)
}} system.out.println(text3);
}}
執行結果是:
thread-1:text1
thread-1:text2
thread-2:text1
thread-1:text2
thread-1:text2
thread-1:text2
.
.
.
我們看thread-1和thread-2都進入了該方法,但是只有thread-1進入了同步塊,thread-2被阻塞了,所以text3也未列印出。我們再看乙個同乙個門衛看守兩塊**的例子:
public class threadtest
});thread t2 = new thread(new runnable()
});t1.start();
t2.start(); }
}class printclass
} }public void printval(int val)
} }}
這段**只能列印出一種結果,原因是這個兩個方法都是門衛this來看管的,他只讓乙個執行緒通過,即使是不同的**塊。其實由this看管的**塊相當於在類的方法上加synchronized(不包括靜態方法哦!),是在類例項上加鎖的。如果是新的例項,那麼this就不是同乙個了。不同的門衛this就可以讓不同的執行緒進入了,這裡就不在舉例了,那我們在看看其他的門衛:
public class threadtest
});thread t2 = new thread(new runnable()
});t1.start();
t2.start(); }
}class printclass
} }public void printval(int val)
} }}
這次是這個類來作門衛了,雖然我們使用了2個類例項(在run方法裡new了兩個例項),但這次還是只列印出一種結果,這就是類同步。就相當於在靜態方法上加synchronized,這個**塊同一時間只允許乙個執行緒訪問。你可以試試將printclass.class換成this,這樣兩個方法都可以列印出結果的。其實新增乙個靜態成員變數,用他做門衛,也可以達到上面demo的效果:
public class threadtest
});thread t2 = new thread(new runnable()
});t1.start();
t2.start(); }
}class printclass
} }public void printval(int val)
} }}
因為靜態成員變數是類持有的,每個例項共享的,所以這樣加鎖與給類本身加鎖效果相同。但要是只是普通的成員變數呢,在這裡我們用string這個物件來舉例:
public class threadtest
});thread t2 = new thread(new runnable()
});t1.start();
t2.start(); }
}class printclass
} }public void printval(int val)
} }}
列印出了2種結果,證明這string物件是被類例項持有的,所以只針對類例項加鎖。但是,試一試將定義改為private string str = ""。呵呵,只列印出一種結果吧。原因可能是jvm虛擬機器優化了string,使之在賦值之後不可變了,所以這個門衛始終就是他一人了。
最後,還要注意一點,方法上的synchronized是不能繼承的哦,子類想要同步,要在方法上自己新增。
java多執行緒(2)
1.synchronized 鎖重入 執行緒請求由自己持有的物件時,如果該鎖是重入鎖,請求就會成功,否則阻塞 2.synchronized出現異常時,鎖自動釋放 3.當多個執行緒要同乙個例項時 雙重校驗鎖 public class dubblesingleton catch interruptede...
Java多執行緒2 執行緒的建立
上一講中我們講了什麼是執行緒,這一講我們細細討論一下關於執行緒的那些事。先看看執行緒的狀態轉換圖。1.首先執行緒被建立出來。2.進入就緒佇列中等待cpu分配時間片 這裡的時間片指的是cpu允許執行緒執行的最大時間 若在規定時間內未執行完成,則執行緒繼續進入就緒佇列等待cpu分配時間片。3.執行緒進入...
Java 回顧筆記 多執行緒 2
執行緒的狀態 執行緒建立的第二種方式 將任務封裝成物件 方法二的好處 區別 繼承 thread和實現 runtimethread 小區別 如果當你想實現4個執行緒同時對乙個元素進行操作,就用run 如果你想4個執行緒 四個元素就用thread 因為當你使用繼承thread的 時候,你就建立了4個物件...