线程同步方式

       一、线程同步介绍

       现实生活中,我们会遇到“同一资源,多个人都想使用”的问题,比如排队打饭,抢票等场景;

在多线程场景下,多个线程访问同一对象,并且部分线程设计修改该对象,这时候我们就需要线程同步机制。

       线程同步其实就是一个等待机制,多个线程同时访问一个对象,此时需要将线程排队,抢夺锁监视器,获得锁监视器的线程先执行,其他线程阻塞,等待;执行完成后释放锁监视器,对列中的线程再去争抢锁监视器,即获得锁监视器的线程才可以执行;仿佛排队上厕所蹲坑,进去一个人把门关上了,其他想上厕所的只能排队等待。

说白了线程同步:就是通过 对列 + 锁的方式实现的。

    二、线程同步的实现方式

     2.1、下面介绍单体项目(单个jvm的情况),线程同步的几种方式:

        2.1.1、同步方法

        语法格式:public synchronized void method(int args){}

        synchronized方法控制对“对象”的访问,必须获取调用该方法的对象锁,该方法才能执行,否则线程阻塞,方法一旦执行,就独占该锁,直到这个方法返回,锁才释放,后边的线程才能获得锁,继续执行;

        synchronized方法弊端:

               1)、若将一个方法声明为synchronized方法,将会影响效率;

               2)  、若给一个方法加synchronized,锁的范围太大,本应该只锁一下改操作,对于读操作不应该加锁。

        2.1.2、 抢车票案例 

 

class ButTick implements Runnable{
    //票
    private int  titck = 10;
    private boolean flag = true;
    @Override
    public void run() {
        //买票
        while(flag){    //1.创建一个循环
            try {
                buy();   //2、执行线程方法
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * synchronized:同步方法   控制对对象的访问 buy这个方法的对象是ButTick 
     * 即为this ,this可以省略
     */
    private synchronized void buy() throws InterruptedException {

        //判断是否有票
        if(titck <= 0){  //3.判断共享数据是否到达了末尾(到了末尾)
            flag = false;
            return;
        }
        Thread.sleep(100);
        //4.判断共享数据是否到达了末尾(没有到末尾,执行核心逻辑)
        System.out.println(Thread.currentThread().getName()+"拿到了第"+titck--+"张票");

    }

        2.2、同步代码块

            语法格式:synchronized(Obj){}

            2.2.1、Obj 锁监视器

                        Obj可以是任何对象,但是推荐使用共享资源作为锁监视器;(一般指被修改的对象)

       执行流程:对多线程并发访问同一资源时;第一个线程,获取锁监视器成功了,执行其中的代码,第二个线程访问资源时,先检查锁监视器是否被占用,被占用则阻塞等待;直到第一个线程执行完任务,释放锁监视器;

           2.2.2 银行取款案例(夫妻二人从同一账户取钱)

   模拟银行取钱

class Drawing extends Thread{
    Account account;//账户

    int drawingMoeny;//取了多少钱

    int nowMoney;//手里的钱

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

    //取钱
    @Override
    public void run() {
        //同步代码块
           synchronized (account){
               if(account.money - drawingMoeny < 0 ){
                   System.out.println(this.getName()+"钱不够取不了==》》》");
                   return;
               }
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               account.money = account.money - drawingMoeny; //卡内余额减去 你取得钱
               //手里的钱
               nowMoney = nowMoney + drawingMoeny;

               System.out.println(account.name+"余额为:"+account.money);

               //Thread.currentThread().getName() == this,getName
               System.out.println(this.getName()+"手里的钱" + nowMoney);
           }
        }
}

模拟账户

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

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

2.3、Lock锁方式

        synchronized同步方法和同步代码块,加锁是隐式的,我们看不到具体在哪加的锁;

为了能更清晰的表达如何加锁、释放锁 jdk1.5以后引入的Lock锁;

        Lock是一个接口,没法实现需要使用它的实现类ReentrantLock()叫做可重入锁

        void lock():获得锁

        void unlock():释放锁

2.3.1、将synchronize修改为lock锁方式代码如下

public class MyLock extends Thread{
    static int ticket = 0;
    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            //添加同步代码块
            //synchronized (MyLock.class){
             lock.lock();
             try{
                 if(ticket < 100){
                         Thread.sleep(10);
                         ticket++;
                         System.out.println(getName() + "正在卖第" + ticket +"票!!!");
                 }else{
                     break;
                 }
             }catch(Exception e){
                 e.printStackTrace();
             }finally{//释放锁
                 lock.unlock();
             }
           // }
        }
    }
}

总结:以上是单体应用下最常见的三种实现线程同步的方式;后边会介绍阻塞对列方式,以及分布式锁实现线程同步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值