Java并发编程-2 对共享资源的访问控制

如果有多个任务同时对同一个资源进行操作,如读写。那么就会存在对共享资源的访问控制问题。例如在火车购票系统中,有多个用户同时购买同一班次的火车票,如果不进行控制,就会出现提交订单时有票,付款时票被别人买走了,所以要在某个用户提交订单到交易完成这段时间内对某张票进行控制,不让其他用户再次购买,这就是并发编程中的加锁问题。在Java中常用的对共享资源的访问方式有如下几种。

1 Java中的synchronized关键字

  • synchronized

    • Java中使用synchronized关键字对共享资源进行控制,当任务要执行用synchronized保护某一代码片段时,首先会检查锁是否可用,然后获取锁执行代码,执行完后释放锁,共享资源可以是内存片段,也可以是文件,输入/输出端口等

    • 每个对象上都含有单一的锁(也称为监视器)。当在对象上调用被synchronized控制的方法时,此对象被加锁,此时此对象上的其它synchronized方法只有在前一个synchronized方法调用完成之后才能被调用。所以对于某个对象所有的被synchronized修饰的方法共享一个锁。在使用并发时,将资源设置为private非常重要,否则,synchronized关键字就不能防止其它任务直接范围资源,这样就会产生冲突。

    • 针对每个类,也有一个锁,作为类的对象的一部分,所以synchronized static方法可以在类的范围内防止对static数据的并发访问。
    • 如果正在写一个变量,它可能接下来被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么必须使用同步,并且读写线程都必须使用相同的监视器锁同步。

2 Lock类(ReentrantLock)

  • Lock对象
    • 可以显示的使用java.util.concurrent.locks中的显式的互斥机制。Lock对象必须显示的创建,锁定和释放,一次和synchronized形式相比它的代码缺乏优雅性。但对于解决某些特定的问题来看它更加灵活
Lock lock = new ReentrantLock();
public int next()
{
    lock.lock();
    try
    {
        //逻辑代码
        return 结果;
    }
    finally
    {
        lock.unlock();
    }
}

return语句必须在try子句中,否则它可能将数据暴露给其它并发执行的任务

 - Lock比synchronized写的代码要复杂,但是当出现异常时它可以做些必要的清理工作,而synchronized仅仅抛出异常。所以只有在有特殊需要时用Lock,如果从代码整洁和不需要处理异常角度考虑,可以直接使用synchronized。

3 原子性

原子操作是不能被线程调度机制中断的操作,一旦操作开始,那么它一定可以在可能发生的上下文切换之前执行完毕。

  • 原子性可以应用于long和double之外的所有基本类型之上的简单操作(读写),JVM可以将64位的读取和写入当作两个分离的32位操作来执行,这就产生了在一个读取和写入操作中间发生上下文切换,从而导致不同任务可以看到不正确的结果,但是当用volatile关键字时,简单的赋值和返回操作就会获得原子性。

4 临界区

有时希望多个线程同时访问方法内部的部分代码而不是整个方法,通过这种方法分离出来的代码被称为临界区,也使用synchronized关键字建立。synchronized被用来指定某个对象,如syncObject可以为this,此对象的锁被用来对花括号内的代码进行同步控制

synchronized(syncObject)
{
    //被需要同步的代码片段
}

5 在其它对象上同步

synchronized必须给定一个在其上进行同步的对象,并且最合理的方式是使用其方法正在被调用的当前对象:synchronized(this),这种方法中,如果获得了synchronized上的锁,那么该对象其它的synchronized方法和临界区就不能被调用了。有时必须在另一个对象上同步,下面例子演示两个任务可以同时进入同一个对象,只要这个对象上的方法实在不同的锁上同步即可,下例中的f和g可以同时运行,因为他们采用不同的同步对象

class Synch{
private Object syncObject = new Object();
public synchronized void f(){};
public void g()
{
    synchronized(syncObject){};
}
};

6 ThreadLocal变量

为了防止任务在共享资源上的冲突,可以为相同的变量在不同的线程都创建不同的存储,例如如果有5个变量都要使用变量x所表示的对象,那么线程本地存储会生成5个用于x的不同存储块。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值