zoukankan      html  css  js  c++  java
  • Java多线程学习——synchronized锁机制

    Java在多线程中使用同步锁机制时,一定要注意锁对对象,下面的例子就是没锁对对象(每个线程使用一个被锁住的对象时,得先看该对象的被锁住部分是否有人在使用)

    例子:两个人操作同一个银行账户,丈夫在ATM机上操作,妻子在银行柜台操作

    账户类:账户里面有100万

    public class Acount {
        public int money=100;
    }

    ATM机类:里面存在一个Acount对象和要取的钱数,在takeMoney方法中加了synchronized 机制

    public class ATM implements Runnable{
        private Acount acount;
        private int withdrawMoney;
    
        public ATM(Acount acount, int withdrawMoney) {
            this.acount = acount;
            this.withdrawMoney = withdrawMoney;
        }
    
        @Override
        public void run() {
            takeMoney();
        }
    
        public synchronized void takeMoney(){    //取钱
            if(acount.money<withdrawMoney){
                return;
            }
            try {
                Thread.sleep(200);  //线程1进入后休眠,线程2仍可以进来,这样可能造成赤字
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"取出"+withdrawMoney+"万元");
            acount.money-=withdrawMoney;
            System.out.println("账户余额:"+acount.money);
        }
    }

    客户类:创建一个唯一账户,两台ATM机(其中一台模拟柜台),分别给两个人使用

    public class Customer {
        public static void main(String[] args) {
            Acount acount=new Acount();
            ATM atm1=new ATM(acount,70);    //丈夫在ATM机上操作账户,妻子在柜台操作账户
            ATM atm2=new ATM(acount,80);
            Thread husband=new Thread(atm1,"丈夫");
            Thread wife=new Thread(atm2,"妻子");
    
            husband.start();
            wife.start();
        }
    }

    运行结果:

    可以看到加了synchronized 后仍然出现线程不安全。

    分析:synchronized 机制一般用在被数据操作的对象中,而takeMoney方法是属于ATM机的方法,在此例子中,一共存在一个账户类,两个ATM机类,两个ATM机类去操作账户类,所以应该把账户类锁住。

     修正:使用同步块机制锁住acount对象

    public void takeMoney(){    //取钱
            synchronized (acount){
                if(acount.money<withdrawMoney){
                    return;
                }
                try {
                    Thread.sleep(200);  //线程1进入后休眠,线程2仍可以进来,这样可能造成赤字
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"取出"+withdrawMoney+"万元");
                acount.money-=withdrawMoney;
                System.out.println("账户余额:"+acount.money);
            }
    
        }

    例子2 Tickert类是线程类

    public class Ticket implements Runnable{
    
        private int ticker=100;
        private boolean flag=true;
    
        @Override
        public void run() {
            while(flag){
                robTicket();
            }
        }
    
        /**
         * 抢票
         */
        public void robTicket(){
            if (ticker <= 0) {
                flag = false;
                return;
            }
            synchronized (this){    //多重验证机制,在这里检测这个对象的这部分代码是否被使用,如果有线程正在使用该对象的该部分代码,就等待
                if (ticker <= 0) {
                    flag = false;
                    return;
                }
                try {   //模拟网络延时
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"-->"+ticker--);
            }
    
    
        }
    }

    主类

    public class RobTicket {
    
        public static void main(String[] args) {
            Runnable ticket=new Ticket();
            //3个线程同时对一个ticket对象进行操作,准确来说是对ticket对象里面的ticket变量操作
            Thread feizhu=new Thread(ticket,"飞猪");
            Thread zhixing=new Thread(ticket,"智行");
            Thread xiecheng=new Thread(ticket,"携程");
    
            zhixing.start();
            feizhu.start();
            xiecheng.start();
        }
    }

    在这个例子里面运用了多重的验证机制,保证了抢票重复和抢票出现负数的情况,如果不加synchronized 里面的if判断语句,仍然会出现线程不安全,因为其他线程可能并发的排队在同步块外面等候了,此时如果还剩一张票的话,当前线程抢完这最后一张票后其他线程仍然有机会抢票,这是不合理的,当然可以为整个方法上锁,但是性能会下降。

  • 相关阅读:
    Element + 列表增删改查
    int 型动态数组
    Vue.prototype.$ 和 Vue.use()
    Vue.js生成一个Vue实例
    Element 树形组件
    使用Vuex 实现标签数字增加与减小
    使用指针做形参
    JavaWeb学习之HttpServletRequest
    JavaWeb学习之HttpServletResponse
    JavaWeb学习之Javaweb核心servlet
  • 原文地址:https://www.cnblogs.com/chiweiming/p/11109078.html
Copyright © 2011-2022 走看看