线程同步

线程同步

多个线程操作同一个资源

并发:同一个对象被多个线程同时操作

形成条件:队列+锁

三大不安全案例

package syn;

//不安全的买票
public class UnSafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"小明").start();
        new Thread(buyTicket,"小白").start();
        new Thread(buyTicket,"小黄").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){
            return;
        }

        //模拟延时
        Thread.sleep(100);

        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);

    }
}

运行结果可能为:

image-20210223094350157

修正:在buy()方法前加上synchronized修饰词

//synchronized同步方法,锁的是this
    private synchronized void buy() throws InterruptedException {
        if (ticketNums <= 0){
            return;
        }
        //模拟延时
        Thread.sleep(100);
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);

    }

package syn;

//不安全的取钱
//两个人去银行取钱

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;
        }

        //sleep可以放大问题的发生性
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

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

运行结果可能为:

image-20210223113343906

修正:将修改对象锁起

//synchronized 默认锁的是this(本身)
    @Override
    public void run() {

        //锁的对象就是变化的量,需要增删改的对象
        synchronized (account){
            //判断有没有钱
            if (account.money-drawingMoney<0){
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
                return;
            }

            //sleep可以放大问题的发生性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

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

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

    }

package syn;

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

//线程不安全的集合
//可能发生两个线程同一瞬间操作了同一个位置,被覆盖了
public class UnSafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);//线程睡眠三秒钟节省了cpu资源,加快list指向更新
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

运行结果可能为:

image-20210223114546109

修正:将list锁住

for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }

同步方法及同步块

  • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块

    同步方法:public synchronized void method(int args){}

  • synchronized方法控制“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。

    缺陷:若将一个大的方法声明为synchronized将会影响效率

posted @ 2021-03-03 16:08  PitayaWalk  阅读(14)  评论(0)    收藏  举报