前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java并发学习2【面试+工作】

Java并发学习2【面试+工作】

作者头像
Java帮帮
发布2018-07-26 14:56:25
3310
发布2018-07-26 14:56:25
举报

Java并发学习2【面试+工作】

三.synchronized&volatile

synchronized

  关键字synchronized的作用是实现进程间的同步。它的工作是对同步的代码加锁,使得每一次,只能有一个线程进入同步块,从而保证线程间的安全性(即同步块每次应该只有一个线程可以执行)。

  关键字synchronized可以有多重用法,这里做一个简单的整理。

  • 制定加锁对象(同一对象)。对给定对象加锁,进入同步代码前需要获得给定对象的锁。
  • 直接作用于实例方法。相当于对当前示例加锁(同一对象),进入同步代码前要获得当前示例的锁。
  • 直接作用于静态方法。相当于对当前类加锁,进入同步代码前要获得当前类的锁。

  学习过《操作系统》的,这里非常容易理解,不在举具体的例子。

  除了用于线程同步、确保线程安全外,synchronized还可以保证线程间的可见性和有序性。从可见性的角度讲,synchronized可以完全替代volatile的功能,只是使用上没有那么方便而已。就有序性而言,被synchronized限制的多线程其实是串行的执行同步代码的。

volatile

  用一句话概括volatile,它能够使变量在值发生改变时能尽快地让其他线程知道.

  首先我们要先意识到有这样的现象,编译器为了加快程序运行的速度,对一些变量的写操作会先在寄存器或者是CPU缓存上进行,最后才写入内存. 而在这个过程,变量的新值对其他线程是不可见的.而volatile的作用就是使它修饰的变量的读写操作都必须在内存中进行!

volatile与synchronized

  • volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
  • volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
  • volatile仅能实现变量的修改可见性,但不具备原子特性,而synchronized则可以保证变量的修改可见性和原子性.
  • volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
  • volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化.

  因此,在使用volatile关键字时要慎重,并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如n=n+1、n++ 等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m + 1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。

  总结:volatile本质是在告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取。可以实现synchronized的部分效果,但当n=n+1,n++等时,volatile关键字将失效,不能起到像synchronized一样的线程同步的效果


四.重入锁&Condition条件

重入锁

  这里介绍一下synchronized、wait、notify方法的替代品(或者说是增强版)-重入锁。重入锁是可以完全替代以上的内容的。并且重入锁的性能是远高于synchronized的,但是jdk6.0开始,jdk对synchronized做了大量的优化,使得两者性能差距不大。

  重入锁使用java.util.concurrent.locks.ReentrantLock类来实现。它的几个重要方法如下:

注意:把解锁操作lock.unlock()放到finally子句非常重要。这样保证即使在临界区的代码抛出了异常,锁也必须释放,否则,其他线程将永远阻塞.

重入锁简单案例: lock与unLock

  上述代码使用重入锁保护临界区资源i,确保了多线程对i操作的安全性。从这段代码可以看到,与synchronized相比,重入锁有着显示的操作过程。开发人员必须手动指定何时加锁,何时释放锁。也正因为这样,重入锁对逻辑控制的灵活性要远远好于synchronized,但值得注意的是,在提出临界区时,必须记得释放锁,否则其他线程就没有机会再访问临界区了。

  对于重入锁,同一个线程可以多次获得锁,但是释放锁的时候,也必须释放相同次数。否则会产生异常。

重入锁的中断响应:lockInterruptibly

锁申请等待限时:lock.tryLock的两个方法

公平锁:ReentrantLock的构造函数可以增加一个参数

Condition条件

  如果大家理解了obj.wait和obj.notify方法的话,那么就很容易理解Condition对象了。它和wait和notify方法的作用是大致相同的。但是wait和notify方法是和synchronized关键字合作使用的,而Condition是与重入锁相关联的。通过Condition的newCondition()方法可以生成一个与当前重入锁绑定的Condition实例。利用Condition对象,我们就可以让线程在合适的时间等待,或者在某一个特定的时间得到通知,继续执行。

Condition提供的基本方法如下:

以上方法的具体含义如下:

  • await() 方法会是当前线程等待,同时释放当前锁,当其他线程中使用signal() 或signalAll() 方法时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。这和obj.wait方法很相似。
  • awaitUninterruptibly() 方法与await() 方法基本相同,只不过它不会在等待过程中响应中断。
  • signal() 方法用于唤醒一个在等待中的线程,这和obj.notify方法很类似。

简单示例

  代码中,听过lock生成一个与之绑定的Condition对象。代码15行要求线程在Condition对象上进行等待。代码32行,由主线程发起通知,告知等待在Condition上的线程可以继续执行了。

  和obj.wait和notify方法一样,当线程使用Condition.await时,要求线程持有相关的重入锁,在Condition.await调用后,这个线程会释放这把锁。同理,在Condition.signal方法调用时,也要求线程先获得相关的锁。在signal方法调用后,系统会从当前Condition对象的等待队列中,唤醒一个线程。一旦线程被唤醒,它会重新尝试获得与之绑定的重入锁,一旦成功获取,就可以继续执行了。因此,在signal方法调用之后,一般需要释放相关的锁,谦让给被唤醒的线程,让他可以继续执行。比如,在本例中,第33行就释放了重入锁,如果省略第24行,那么,虽然已经唤醒了线程t1,但是由于它无法重新获得锁,因而也就无法真正的继续执行。

  在jdk内部,重入锁和Condition对象被广泛的使用,后面讲到的线程安全的容器,他们的内容时候都有重入锁和Condition对象的影子。


五.信号量

信号量为多线程协作提供了更为强大的控制方法。广义上说,信号量是对锁的扩展。无论是内部锁synchronized还是重入锁ReentrantLock,一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问某一个资源。信号量主要提供了一下构造函数:

  在构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。当每个线程每次只申请一个许可时,这就相当于指定了同时有多少个线程可以访问某一个资源。信号量的主要逻辑方法有:

这里只讲几个常用的方法:

  • acquire() 方法尝试获得一个准入的许可。若无法获得,则线程会等待,直到有线程释放一个许可或者当前线程被中断。
  • acquireUninterruptibly方法和acquire方法类似,但是不响应中断。
  • tryAcquire尝试获得一个许可,如果成功返回true,失败返回false,它不会进行等待,立即返回。
  • release用于在线程访问资源结束后,释放一个许可,以使其他等待许可的线程可以进行资源访问。

一个案例:

  上述代码中,15、16行为临界区,程序会限制执行这段代码的线程数。这里在第7行,声明了一个包含5个许可的信号量。这就意味着同时可以有5个线程进入代码段15,16行。申请信号量使用semp.acquire,在离开时,务必使用semp.release释放信号量。这就和释放锁一个道理。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java帮帮 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 三.synchronized&volatile
  • synchronized
  • volatile
  • volatile与synchronized
  • 四.重入锁&Condition条件
  • 重入锁
    • 重入锁简单案例: lock与unLock
      • 重入锁的中断响应:lockInterruptibly
        • 锁申请等待限时:lock.tryLock的两个方法
          • 公平锁:ReentrantLock的构造函数可以增加一个参数
          • Condition条件
            • 简单示例
              • 一个案例:
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档