多線程購票問題

購票問題

題目: 三個售票員 賣出 30張票

從購票問題入手,購票問題涉及到多線程併發問題,多線程同時訪問賣票資源,怎麼保證數據一致性是最重要的問題。

解決方案:

  1. 在方法上添加 synchronized 同步標誌控制線程訪問,一次只能有一個線程訪問該方法。
  2. 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 也實現了多線程併發有序訪問統一資源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章