😋线程不安全问题产生的原因:由于同一个进程的不同线程共享了同一块存储空间, 避免不了引起访问冲突的问题, 为了 保证数据的在访问时的正确性, 在访问时加入锁机制(synchronized), 当一个线程获得对象的🔒的时候, 此线程独占对象的资源, 其他线程必须等待此线程释放锁才能继续进行.

是用锁同时也会带来一些问题:

  • 一个线程持有锁, 会导致其他所有需要这个锁的线程挂起.
  • 会引发系统的调度延时, 或进行太多的上下文切换, 带来性能问题.
  • 若一个优先级高的线程在等待一个优先级低的线程释放锁, 会引起性能倒置.

😋同步方法:synchronized关键字的两种用法:

  • synchronized方法
public synchronized void mothod() {
    // 此时synchronized是对this加🔒
    // 如果把一个很多逻辑的方法声明为synchronized方法, 将会影响效率.
}
  • synchronized块
synchronized(obj){
   // obj: 需要🔒的对象, 也称之为同步监视器
}
  • 只有当方法里面有修改对象数据的内容时才加锁(新增, 修改, 删除), 锁的太多,浪费资源.

💛线程不安全案例1: 几个人同时抢车票.

package com.smile.test.thread.unsafethread;

public class UnSafeBuyTickets {
    public static void main(String[] args) {
        BuyTickets buyTickets = new BuyTickets();
        new Thread(buyTickets,"小明").start();
        new Thread(buyTickets,"小红").start();
        new Thread(buyTickets,"小黑").start();
    }
}
class BuyTickets implements Runnable{
    private int ticletsNum = 10;
    private boolean flag = true;
    // 线程不安全的做法
    @Override
    public void run() {
        while(flag){
            buy();
        }
    }
    /*
    线程安全的做法
    @Override
    public synchronized void run() {
        while(flag){
            buy();
        }
    }
    */
    // 让线程停止
    private void stop(){
        this.flag = false;
    }
    // 买车票
    private void buy(){
        if (ticletsNum <= 0) {
            System.out.println("卖完了");
            stop();
            return;
        }
        System.out.println(Thread.currentThread().getName()+"拿到"+ ticletsNum--);
    }
}
/*
第一次:
小明拿到10
小明拿到9
小明拿到8
小明拿到7
小明拿到6
小明拿到5
小明拿到4
小明拿到3
小明拿到2
小明拿到1
卖完了
小红拿到0
卖完了

Process finished with exit code 0
第二次:
小明拿到10
小黑拿到8
小红拿到9
小黑拿到6
小明拿到7
小黑拿到4
小红拿到5
小黑拿到2
小明拿到3
卖完了
卖完了
小红拿到1
小明拿到0

Process finished with exit code 0
第三次:
小红拿到10
小红拿到9
小红拿到7
小红拿到6
小明拿到10
小红拿到5
小黑拿到8
小黑拿到2
小红拿到3
小明拿到4
卖完了
小明拿到0
卖完了
小黑拿到1

Process finished with exit code 0
*/

💛线程不安全案例2: 银行取钱.

package com.smile.test.thread.unsafethread;

public class GetBankMoney {
    public static void main(String[] args) {
        Account account = new Account("my Account", 100);
        Bank me = new Bank(account, 50);
        Bank my = new Bank(account, 100);
        new Thread(me,"我自己").start();
        new Thread(my,"我对象").start();
    }
}
class Account{
    // 账户
    public String name;
    // 余额
    public int money;

    public Account(String name, int money) {
        this.name = name;
        this.money = money;
    }
}
class Bank implements Runnable{
    Account account; // 账号
    int needMoney; // 取多少钱

    public Bank(Account account, int needMoney) {
        this.account = account;
        this.needMoney = needMoney;
    }
    // 线程不安全的做法
    @Override
    public void run() {
        if (account.money - needMoney < 0) {
            System.out.println("余额不足");
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money = account.money - needMoney;
        System.out.println(Thread.currentThread().getName() + "取出" + needMoney);
        System.out.println(account.name + "余额:" + account.money);
    }
    /*
    线程安全的做法
    @Override
    public void run() {
        synchronized (account){
            if (account.money - needMoney < 0) {
                System.out.println("余额不足");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money = account.money - needMoney;
            System.out.println(Thread.currentThread().getName() + "取出" + needMoney);
            System.out.println(account.name + "余额:" + account.money);
        }
    }
    */
}
/*
我自己取出50
我对象取出100
my Account余额:-50
my Account余额:-50

Process finished with exit code 0
*/

💛线程不安全案例3: ArrayList类---------输出为啥不是5000?

package com.smile.test.thread.unsafethread;

import java.util.ArrayList;
import java.util.List;

public class UnSafeArrayList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
            for (int i = 0; i < 5000; i++) {
                new Thread(()->{
                    // 线程不安全
                    list.add(Thread.currentThread().getName());
                    /*
                    线程安全的做法
                    synchronized (list){
                        list.add(Thread.currentThread().getName());
                    }
                    */
                }).start();
            }

        System.out.println(list.size());
    }
}
/*
这也是一种线程安全的做法
 CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 5000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
*/
/*
4579

Process finished with exit code 0
*/