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
posted @ 2021-10-02 16:03  酥炸小黄瓜  阅读(158)  评论(0)    收藏  举报