購票問題
題目: 三個售票員 賣出 30張票
從購票問題入手,購票問題涉及到多線程併發問題,多線程同時訪問賣票資源,怎麼保證數據一致性是最重要的問題。
解決方案:
- 在方法上添加 synchronized 同步標誌控制線程訪問,一次只能有一個線程訪問該方法。
- JDK1.5後提供的JUC(Java併發包)。
個人進行多線程編程按照三步驟:
在高內聚低耦合的前提下: 線程-----操作-----資源類
線程:創建線程對象
操作:調用資源類對外暴露的調用方法
資源類:線程操作的對象
Synchronized 同步
定義一個資源類,票數初始量和賣票方法,在賣票方法加上 Synchronized 同步標誌。
class Ticket01{
//30張票
private int number = 30;
//賣票
public synchronized void saleTicket(){
if(number > 0){
System.out.println(Thread.currentThread().getName() + "\t賣出第" + (number--) + "票,還剩下" + number + "張");
}
}
}
Main方法
創建三個線程同時調用 Ticket01 類的賣票方法,輸出結果如下:
public static void main(String[] args) {
Ticket01 ticket = new Ticket01();
//new Thread(Runable runable,String name)
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 1; i <= 40; i++){
ticket.saleTicket();
}
}
}, "A").start();
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 1; i <= 40; i++){
ticket.saleTicket();
}
}
}, "B").start();
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 1; i <= 40; i++){
ticket.saleTicket();
}
}
}, "C").start();
}
由於線程搶佔資源是隨機的,每次調用的結果都是不同的。可以看出線程調用是有序的,說明 Synchronized 控制線程有序訪問是成功的。
JUC
定義一個資源類,票數初始量和賣票方法,通過 JUC 提供的 Lock 接口和 ReentrantLock 實現類對方法進行加鎖操作。
Lock 接口是 JUC 提供的鎖接口,相比 Synchronized 同步功能更加強大,提供了更靈活的結構化,主要使用的實現類是 ReentrantLock 可重入鎖。
class Ticket02{
//30張票
private int number = 30;
//創建鎖對象,可重入鎖
Lock lock = new ReentrantLock();
//賣票
public void saleTicket(){
//加鎖流程
lock.lock();
try {
if(number > 0){
System.out.println(Thread.currentThread().getName() + "\t賣出第" + (number--) + "票,還剩下" + number + "張");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
Main方法
創建三個線程同時調用 Ticket02 類的賣票方法,輸出結果如下:
public static void main(String[] args) {
Ticket02 ticket = new Ticket02();
//new Thread(Runable runable,String name)
new Thread(() -> {for (int i = 1; i <= 20; i++)ticket.saleTicket();}, "A").start();
new Thread(() -> {for (int i = 1; i <= 20; i++)ticket.saleTicket();}, "B").start();
new Thread(() -> {for (int i = 1; i <= 20; i++)ticket.saleTicket();}, "C").start();
}
可以看出每個線程搶佔資源後調用資源方法是有序的執行,Lock 也實現了多線程併發有序訪問統一資源。