三大线程不安全案例

HeJin大约 2 分钟Java多线程详解

买票

public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station,"苦逼的我").start();
        new Thread(station,"牛逼的你们").start();
        new Thread(station,"可恶的黄牛党").start();
    }
}

class BuyTicket implements Runnable{
    /** 票 **/
    private int ticketNums = 10;
    /** 外部停止标识 **/
    boolean flag = true;

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

    private void buy() throws InterruptedException {
        // 判断是否有票
        if (ticketNums <= 0){
            flag = false;
            return;
        }
        Thread.sleep(100);
        // 买票
        System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
    }
}
// 出现-1
牛逼的你们拿到了第-1张票

Process finished with exit code 0    

// 多个人拿一张票
牛逼的你们拿到了第2张票
可恶的黄牛党拿到了第2张票    

测试发现有多个人拿到同一张票和出现了负数,线程不安全

每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

银行取钱

public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"结婚基金");
        Drawing you = new Drawing(account, 50,"你");
        Drawing girlFriend = new Drawing(account, 100,"girlFriend");

        you.start();
        girlFriend.start();
    }
}

class Account{
    /** 余额 **/
    int money;
    /** 卡名 **/
    String name;

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

class Drawing extends Thread{
    /** 账户 **/
    Account account;
    /** 取多少钱 **/
    int drawingMoney;
    /** 现在手里有多少钱 **/
    int nowMoney;

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

    /**
     * 取钱
     */
    @Override
    public void run() {
        // 判断有没有钱
        if (account.money - drawingMoney < 0){
            System.out.println(Thread.currentThread().getName() + "钱不够了,取不了");
            return;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 卡内余额 = 余额 - 取的钱
        account.money = account.money - drawingMoney;
        // 手里的钱
        nowMoney = nowMoney + drawingMoney;

        System.out.println(account.name+"余额为:"+account.money);
        System.out.println(this.getName()+"手里的钱:" + nowMoney);
    }
}

运行结果不一样,可能是CPU多线程执行了。

结果1:

结婚基金余额为:50
你手里的钱:50
结婚基金余额为:-50
girlFriend手里的钱:100

Process finished with exit code 0

结果2:

结婚基金余额为:-50
结婚基金余额为:-50
girlFriend手里的钱:100
你手里的钱:50

Process finished with exit code 0

线程不安全的集合

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
9999

Process finished with exit code 0

同时有两个或者多个线程一瞬间同时操作了同一个位置,也就是把多个数据添加到了同一个位置,会覆盖掉,list的长度就会减小。