并发编程之线程同步锁

     线程同步的风险

     使用多线程可以让你充分利用多核处理器的资源,更简单的处理异步事件等。但是多线程有3个非常需要关注和解决的风险:

    1.安全性风险:即每一次系统运行的最终结果都是与预期相符的,不会产生各种稀奇古怪无法预知的结果。

    2.活动度风险:即预期会发生的事件最终会发生,而不是进入一种永远不会发生的状态。一个典型的事件就是死锁。

    3.性能风险:良好的多线程设计能获得良好的性能,但是设计不好的系统反而可能使性能下降。多线程的性能开销主要来源于两个方面:1是线程同步带来的开销,2是线程上下文切换带来的开销。

      而上述3大风险的解决或产生,无疑都与线程同步息息相关。安全性需要线程同步来解决,线程同步可能发生死锁导致活跃度失败的风险,即使解决了安全性和避免了活跃度失败的风险,这时候系统可以实现你的功能了,但是也并不意味着你线程同步没有问题,还有可能系统性能大大降低了。

    可以说线程同步是并发编程设计中最核心的问题,下面讲讲几种常用的线程同步方式

volatile关键字

 

       线程同步包含两方面的含义:一是可见性,保证获得的数据时最新的;二是原子性,原子性是不可分割的最小粒度的操作步骤。操作过程中只有一个线程在运行,不可被其他线程中断、更改。在没有同步的情况下,一个最小粒度的原子是一个机器指令。而线程同步可以使一组操作指令成为原子性。volatile关键字是一种比较弱的同步方式,他不会对它声明的对象加锁,也不会线程造成阻塞,它只保证可见性,不保证原子性。用volatile声明的属性只保证你读到的是最新的值,并不能保证a++是同步的。所以,为防止volatile被滥用,一般满足以下3个条件才能安全的使用volatile:

1,写入新的值时,并不依赖于当前的值;或者能保证只有一个线程能修改值。

2,变量不需要与其他的变量共同参与不变性约束。

3,访问变量时,没有其他的原因需要加锁。

 

原子类

     java为大部分基本类型提供了原子类。比如AtomicInteger, AtomicLong和AtomicBoolean。这些类用法基本相似,他不当保证了get/set的同步,同时还为常用的自增自减操作提供了同步方法decrementAndGet,getAndDecrement,incrementAndGet,getAndIncrement

信号量Semaphore

      当一个线程想要访问某个共享资源,首先,它必须获得semaphore。如果semaphore的内部计数器的值大于0,那么semaphore减少计数器的值并允许访问共享的资源。计数器的值大于0表示,有可以自由使用的资源,所以线程可以访问并使用它们。如果semaphore的计数器的值等于0,那么semaphore让线程进入休眠状态一直到计数器大于0。计数器的值等于0表示全部的共享资源都正被线程们使用,所以此线程想要访问就必须等到某个资源成为自由的。Semaphore对象的构造参数可以传信号上线,当上限为1时就相当于锁。semaphore.acquire()获取信号,该方法阻塞一定到其他线程释放信号。semaphore.release()执行完锁住的代码之后,需要释放信号,让其他等待的线程能够获取访问资格。

synchronized关键字

synchronized声明方法

      只有一个执行线程将会访问一个对象中被synchronized关键字声明的方法。如果另一个线程试图访问同一个对象中任何被synchronized关键字声明的方法,它将被阻塞,直到第一个线程结束方法的执行。几点说明:1.只要有一个线程锁住了对象中的一个synchronized方法,那么其他线程将不能访问这个对象中的任何一个被声明为synchronized的方法。 对于synchronized声明的方法,锁是当前实例对象。

2.如果静态方法被声明为synchronized,此时锁是当前类的class对象。只有一个线程能够访问该静态方法。但是,其他线程可以访问该类的一个对象中的其他非静态的方法。 你必须非常小心这一点,因为两个线程可以访问两个不同的同步方法,如果其中一个是静态的而另一个不是。如果这两种方法改变相同的数据,你将会有数据不一致 的错误。

3.由于synchronized会锁住这个方法的整个代码,所以一般适合于比较简单、执行效率高的方法,否则将可能导致性能严重下降。

synchronized锁住一段代码

      由于某些方法可能比较复杂,synchronized声明方法会使性能下降,所以可以使用synchronized包含需要同步的代码段,此时锁是synchronized括号里包含的对象当你使用synchronized关键字来保护代码块时,必须通过一个对象的引用作为参数,比如经常用的是synchronized(this){code}。但是如果对象中有两个以上的属性需要独立上锁时,用同一个锁则会对性能造成影响。这时可以在对象中创建多个object对象,通过声明不同的synchronized(object1){code1},synchronized(object2){code2}来分别给不同的属性操作方法上锁。

 

使用Lock

      Lock比synchronized关键字更加强大、灵活。很多用的比较多的锁,比如可重入锁,读写锁都在用Lock实现的。同时Lock还提供了tryLock方法来获得锁,并立即返回,不会使线程休眠。如果tryLock返回true,表示可以获取锁,如果返回false,则不能获得锁,开发人员可以根据场景对不同结果进行处理。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值