java线程安全的实现方法_笔记

阅读《深入理解java虚拟机》后的阅读笔记。

1、 互斥同步

同步时指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个(或者是一些,使用信号量的时候)线程使用。而互斥是实现同步的一种手段,临界区,互斥量和信号量都是主要的互斥实现方式。

  1.1 在java中最基本的互斥同步手段就是synchronized关键字,synchronized关键字经过编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令。根据虚拟机规范要求,在执行monitorenter指令时,首先要尝试获取对象的锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数加1,相应的,在执行monitorexit指令时会将锁计数器减1,当计数器为0时,锁就被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。

注意:

因为java的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多的处理器时间。多以synchronized是java语言中一个重量级(Heavyweight)的操作。

  1.2 使用java.util.concurrent包中的重入锁(ReentrantLock)来实现同步。相比synchronized,ReentrantLock增加了一些高级功能,主要有以下三项:

     等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情

     可实现公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁

     锁可以绑定多个条件:一个ReentrantLock对象可以同时绑定多个Condition对象

2、 非阻塞同步

互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,这是一种悲观的并发策略。总是认为只要不去做正确的同步措施(加锁),那就肯定会出问题,无论共享数据是否真的会出现竞争,它都要进行加锁、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要被唤醒等操作。

随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略。先进行操作,如果没有其他线程征用数据,那操作就成功了;如果共享数据有征用,产生了冲突,那就再进行其他的补偿措施。这种乐观的并发策略的许多实现不需要线程挂起,所以被称为非阻塞同步。

3、无同步方案

  3.1 可重入代码:这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断他,转而去执行另外一段代码(包括递归调用它本身),它可以保证原来的程序不会出现任何错误。

  3.2 线程本地存储:如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行?如果能保证,我们就可以把共享数据的课件范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用问题。(例如:“生产者-消费者”模式,都会将产品的消费过程尽量在一个线程中消费完)



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值