多线程同步机制

为什么要引入同步机制

在多线程环境中,可能会出现两个甚至更多的线程试图同时访问同一个资源。必须对这种潜在的资源冲突进行预防。

解决方法

在线程使用一个资源时为其加入锁机制。访问资源的第一个线程对其加上锁之后,其他线程便不能再使用那个资源,除非被解锁。

例子

比如银行取钱的例子:
首先有一个银行账户:

/**
 * 银行账户类,里面的余额为1000
 */
class Bank {
    private int balance = 1000;
    /**
     * 取钱的方法
     * @param number 取钱的金额
     * @return
     */
    public  int getMoney(int number) {
        if (number < 0) {
            return -1;
        } else if (number > balance) {
            return -2;
        } else if (balance < 0) {
            return -3;
        } else {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            balance -= number;
            System.out.println("balance is:" + balance);
            return number;
        }
    }
}

取钱,可以可以通过柜台取钱,可以通过ATM取钱

/**
 * ATM取钱
 */
class AtmFetchMoney extends Thread {
    private Bank bank;
    public AtmFetchMoney(Bank bank) {
        this.bank = bank;
    }
    @Override
    public void run() {
        System.out.println("ATM取钱:" + bank.getMoney(800));
    }
}

/**
 * 柜台取钱
 */
class GuiTaiFetchMoney extends Thread {
    private Bank bank;
    public GuiTaiFetchMoney(Bank bank) {
        this.bank = bank;
    }
    @Override
    public void run() {
        System.out.println("柜台取钱:" + bank.getMoney(800));
    }
}

现在开始模拟ATM和柜台同时在一起账户进行取钱的过程:

public class FetchMoney {
    public static void main(String[] args) {
        /**
         * 模拟取钱过程,两个线程同时对一个账户进行操作。
         */
        Bank bank = new Bank();
        Thread t1 = new AtmFetchMoney(bank);
        Thread t2 = new GuiTaiFetchMoney(bank);
        t1.start();
        t2.start();
    }
}

结果1:

balance is:-600
balance is:-600
柜台取钱:800
ATM取钱:800

-600 -600 第一个线程进来之后减去800之后,没有执行输出余额,那么第二个也减去了800,所以最后分别输出就是-600 -600
结果2:

balance is:200
balance is:200
ATM取钱:800
柜台取钱:800

200 200第一个线程进来之后800之后,没有减去余额,那么第二个线程进来之后拿到余额1000-800=200,所以最后分别输出就是200 200
结果3:

balance is:200
ATM取钱:800
balance is:-600
柜台取钱:800

200 -600第一个线程进来之后没有减去800,输出余额200,那么第二个线程进来之后拿到余额200-800=-600,最后输出的是200 -600
通过上面的例子发现,多线程中如果对同一个资源进行操作会出现资源竞争的情况,该如何解决呢?

引入同步机制synchronized

由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问,利用synchronized关键字。

改造上面的例子:

在方法前面加上synchronized关键字

   public synchronized  int getMoney(int number) {
        //对方法加上synchronized关键字
        if (number < 0) {
            return -1;
        } else if (number > balance) {
            return -2;
        } else if (balance < 0) {
            return -3;
        } else {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            balance -= number;
            System.out.println("balance is:" + balance);
            return number;
        }
    }

输出结果:

balance is:200
ATM取钱:800
柜台取钱:-2

可以看到ATM取钱之后,余额只剩下200,柜台再去取钱的时候就返回-2,表示余额不足,不会出现了余额不正确的情况。

synchronized需要明确的几个问题:

1.synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果 再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
2.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
3.每个对象只有一个锁(lock)与之相关联。
4.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

synchronized作用域:

1。某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线 程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
2。某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值