Java多線程的經典案例,3個窗口同時賣100張火車票。要保證沒有重複票和錯票。
錯誤案例:
public class TestSellTicketsRunnable {
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread thread1 = new Thread(tickets, "窗口1");
thread1.start();
Thread thread2 = new Thread(tickets, "窗口2");
thread2.start();
Thread thread3 = new Thread(tickets, "窗口3");
thread3.start();
}
}
class Tickets implements Runnable {
static int countTickets = 100;
@Override
public void run() {
while (true) {
if (countTickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣票,票號是:" + countTickets);
countTickets--;
} else {
break;
}
}
}
}
運行的結果如下圖所以,可以看到會導致有重複票和錯誤票產生。(如果不寫sleep()方法,不一定能出現錯誤的現象)
原因分析:
線程1執行了if方法之後,CPU執行權限被線程2搶去了,然後線程2開始執行。當線程2執行到if之後,又被線程3搶去了。然後線程3執行完了打印方法,CPU執行權限被線程1搶去了,線程1執行打印方法,這個時候的票數已經出現異常了。等線程1執行完了,輪到線程2的時候,又會出現票數異常。所以,可以看到最後兩個打印的結果是錯誤的結果。
正確寫法:
public class TestSellTicketsRunnable {
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread thread1 = new Thread(tickets, "窗口1");
thread1.start();
Thread thread2 = new Thread(tickets, "窗口2");
thread2.start();
Thread thread3 = new Thread(tickets, "窗口3");
thread3.start();
}
}
class Tickets implements Runnable {
static int countTickets = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (countTickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣票,票號是:" + countTickets);
countTickets--;
} else {
break;
}
}
}
}
}
這裏加上了 synchronized (this) 這麼一段代碼,叫做線程同步,也可以叫做鎖。保證多線程在處理同一個對象(或者說同一個資源)的時候,會把synchronized()裏面大括號包的內容執行完,再輪到下個線程。
打印結果如下: