并发:同一个对象被多个线程同时操作(抢票)
每个对象都有一把锁,sleep不会释放锁
线程同步:多个线程操作同一个资源
线程同步的条件:队列和锁(安全性高,但会损失性能)
 例子:队列(排队上厕所)和锁(为保证安全,一次只能有一个上厕所)
同一进程的多个线程共享同一块存储空间,使用方便,但会带来访问冲突,锁机制(synchronized)可保证数据在方法中被访问时的正确性。当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁,此时会存在问题
synchronized关键字两种用法
不安全的买票案例
//线程不安全
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 = 100;
    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(1000);
        //买票
        System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
    }
}
 
同一张票会被多人抢到,说明线程是不安全的
 添加同步锁后
    private synchronized void buy() throws InterruptedException {
        //判断是否有票
        if(ticketNums <= 0){
            flag = false;
            return;
        }
 

 同一张票只能被一个人拿到
不安全的取款案例
public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100,"教育基金");
        Drawing me = new Drawing(account, 80, "我");
        Drawing you = new Drawing(account, 70, "你");
        me.start();
        you.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);
    }
}
 

 银行账户出现负数,当两个线程同时看到账户余额足够取的时候,都将自己需要的数字拿到了自己的内存里,此时线程是不安全的
 为账户account添加同步锁后
synchronized (account) {
            //判断是否有钱
            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);
        }
 

线程不安全的集合
//线程不安全的集合
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                    list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(1000);
        System.out.println(list.size());
    }
}
 

 10000个线程没有被全部添加,在添加过程中,会出现集合的某个位置同时添加两个线程的情况,后来的线程被覆盖掉了,少的这8个其实是被覆盖掉了
            synchronized (list) {
                list.add(Thread.currentThread().getName());
            }
 

扩展
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(1000);
        System.out.println(list.size());
    }
}
 










