在兩個線程共賣100張票的時候,也會出現問題,同時賣同一張票。
程序如下:
public class TestThread implements Runnable{
private int j=100;
public void run(){
for(int i = 0 ;i<100;i++){
if(j>0){
try {
Thread.sleep(300);//注意這裏休眠了300毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("賣第"+j+"張票!");
j--;
}
}
}
}
public static void main(String[] args) {
TestThread m=new TestThread();
new Thread(m).start();
new Thread(m).start();
}
結果:
賣第100張票!
賣第100張票!
賣第98張票!
賣第98張票!
......
造成此問題的根本原因在於,判斷剩餘票數和修改票數之間加入了延遲操作。
如果想解決這樣一個問題,就必須使用同步,所謂的同步就是指多個操作在同一時間段內只能有一個線程進行,其他線程要等待此線程完成之後纔可以繼續執行。
在Java中可以通過同步代碼的方法進行代碼的加鎖操作,同步的實現由兩種方式:同步代碼塊和同步方法
1. 同步代碼塊:使用synchronized關鍵字進行同步代碼塊的聲明,但是在使用此操作時必須明確的指出到底要鎖定的是哪個對象,一般都是以當前對象爲主:
synchronized(對象){//一般都是將this進行鎖定
需要同步的代碼;
}
使用同步代碼塊修改之前的程序:
public class TestThread implements Runnable{
private int j=100;
public void run(){
for(int i = 0 ;i<100;i++){
synchronized (this) {//同步代碼塊,要將if判斷條件包括在其中,否則會出現賣出第0張票的問題
if(j>0){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("賣第"+j+"張票!");
j--;
}
}
}
}
}
2. 同步方法:將要同步的代碼塊抽取成方法
public class TestThread implements Runnable {
private int j = 100;
public void run() {
for (int i = 0; i < 100; i++) {
sale();
}
}
public synchronized void sale() {//同步方法
if (j > 0) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("賣第" + j + "張票!");
j--;
}
}
}