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判断语句,仍然会出现线程不安全,因为其他线程可能并发的排队在同步块外面等候了,此时如果还剩一张票的话,当前线程抢完这最后一张票后其他线程仍然有机会抢票,这是不合理的,当然可以为整个方法上锁,但是性能会下降。

原文地址:https://www.cnblogs.com/chiweiming/p/11109078.html