1. 程式人生 > 其它 >Java多執行緒(五)-執行緒同步(三大不安全案例)

Java多執行緒(五)-執行緒同步(三大不安全案例)

六.執行緒同步(併發)

處理多執行緒問題時,多個執行緒訪問或修改同一個物件(併發)(可理解為現實世界中的搶票),此時就需要執行緒同步。

所謂執行緒同步,就是多個需要同時訪問同一個物件的執行緒進入這個物件的等待池形成佇列,等待佇列前面的執行緒使用完畢,後面的執行緒再接著使用。同時為了保證物件訪問的正確性,引入了鎖機制。當一個執行緒獲得物件的排它鎖,獨享資源,其他執行緒需等待該執行緒釋放鎖。可能存在以下問題:

  • 一個執行緒持有鎖會導致其他所有需要此鎖的執行緒掛起
  • 加鎖,釋放鎖會導致過多的上下文切換和排程延時,影響效能
  • 若優先順序高的執行緒等待低的執行緒釋放鎖,會引起優先順序倒置,影響效能

三大不安全示例

原因:每個執行緒在自己的工作記憶體互動,記憶體控制不當造成資料不一致。

買票
public class Unsafe1 {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"a").start();
        new Thread(buyTicket,"b").start();
        new Thread(buyTicket,"c").start();
    }
}

class BuyTicket implements Runnable{

    private int ticketNum = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void buy() throws InterruptedException {
        if (ticketNum <= 0){
            flag = false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName()+"獲得了第"+ticketNum--+"張票");
    }
}
執行結果
b獲得了第9張票
a獲得了第10張票
c獲得了第8張票
b獲得了第7張票
c獲得了第6張票
a獲得了第5張票
b獲得了第3張票
c獲得了第2張票
a獲得了第4張票
b獲得了第0張票
a獲得了第1張票
c獲得了第-1張票
取錢
public class Unsafe2 {
    public static void main(String[] args) {
        Account account = new Account(200,"home");
        // 建立家庭賬戶一共有200w
        GetMoney boy = new GetMoney(account,200,"boy");
        GetMoney girl = new GetMoney(account,50,"girl");
        boy.start();
        girl.start();
    }
}

class Account{

    int money;
    String id;

    public Account(int money, String id) {
        this.money = money;
        this.id = id;
    }
}

class GetMoney extends Thread{

    Account account;
    int get;
    // 取了多少
    int now;
    // 現餘多少

    public GetMoney(Account account, int get,String name) {
        super(name);
        this.account = account;
        this.get = get;
    }

    @Override
    public void run() {
        if (account.money - get < 0){
            System.out.println(Thread.currentThread().getName()+":money is not enough");
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money = account.money - get;
        now = now + get;
        System.out.println(account.id+"-account-money:"+account.money);
        System.out.println(Thread.currentThread().getName()+"-now:"+now);
    }
}
執行結果
home-account-money:-50
home-account-money:-50
boy-now:200
girl-now:50
list
public class Unsafe3{
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
               list.add(Thread.currentThread().getName());
               // 多個執行緒可能同時操縱同一個位置,導致資料被覆蓋               
            }).start();
        }
        System.out.println(list.size());
    }
}
執行結果
996