參考網址(寫得非常好,珍藏網址之一):http://www.blogjava.net/tscfengkui/archive/2010/11/10/337709.html?opt=admin
當兩個以上的線程對同一個對象進行操作時,這個對象的屬性就難以控制了,會出現很多意外的結果。
舉一個在多線程操作當中經典的例子,電影院售票問題。
電影院的窗口可以同時售票,售出的票不能重疊,即不能售出兩張11排11號的位置。
class Source implements Runnable {
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
if( ticket>0){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System. out.println(Thread. currentThread().getName()+ "號窗口售出" + "第" + ticket-- + "張票");
}
}
}
}
public class test9 {
public static void main(String[] args) {
//只能新建一個source,確保三個線程都是對同一個source操作
Source source = new Source();
new Thread(source, "A").start();
new Thread(source, "B").start();
new Thread(source, "C").start();
}
}
可以看到C、B都售出標記爲1號的票,因爲線程C售出1號的票後,沒來得及自減,就被線程B訪問了!這明顯不符合業務的邏輯。
我們需要這樣一套機制,當資源正在被某一線程訪問的時候,資源被鎖住,其他線程訪問不了,直到被訪問的線程退出使用該資源。
原理圖:
很幸運的是,Java已經幫我們實現了這個機制,只要使用synchronized關鍵字就完美解決了這個問題。程序員不用去管Java內部怎麼實現這個機制的,只要使用它就可以了。
synchronized的用法:
class Source implements Runnable {
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sale();
}
}
public synchronized void sale() {
if( ticket>0){
System.out.println(Thread.currentThread().getName()+ "號窗口售出" + "第" + ticket -- + "張票" );
}
}
}
結果:
2、構成同步塊
class Source implements Runnable {
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized ( this) {
if( ticket>0){
System. out.println(Thread. currentThread().getName()+ "號窗口售出" + "第" + ticket-- + "張票" );
}
}
}
}
}
結果:
VIP:synchronized 的位置非常關鍵,在不同的地方有不同的效果,以下幾個例子,讀者自行分析。
eg4:
class Source implements Runnable {
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
if( ticket>0){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized( this){
System. out.println(Thread. currentThread().getName()+ "號窗口售出" + "第" + ticket-- + "張票" );
}
}
}
}
}
結果:
class Source implements Runnable {
private int ticket = 10;
@Override
public synchronized void run() {
// TODO Auto-generated method stub
for( int i = 0; i < 50; i++){
if( ticket>0){
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System. out.println(Thread. currentThread().getName()+ "號窗口售出" + "第" + ticket-- + "張票" );
}
}
}
}
結果: