因为两周没更新了...

也不是懒,这两周确实有些忙,赶项目进度赶的不亦乐乎...

终于赶在工期前,可以进入内测了,我也有了些时间,可以更新啦...

线程并发锁是很常见的问题,而且在Java中锁的类型、概念、使用场景等等也是面试必问的,所以今天就来先简单的说一说线程并发中常用的一下锁。

0 1

公平锁

何为公平锁?就字面来理解,它就是一种有公平机制,并且不会因为你有任何的“背景”、“关系”就可以为所欲为的锁。在并发环境下,每个线程在获取锁时先会看该锁维护的等待队列,如果为空,或者当前线程时等待队列的第一个,就占有锁,否则就会加入到等待队列中,此后所有的线程都是以FIFO(先进先出)的规则来执行。

在Java中提供了公平锁的实现类,先看个代码demo:

打印值:

可以看到线程获取到锁一定和启动保持一致的,另外ReentrantLock有参构造默认是true,也就是开启公平锁,

还有一点,代码中有虚线的问题,是因为我创建线程是显式的,alibaba检测编码规约是提示,必须使用线程池的方式创建线程,避免出现OOM。

优点

那公平锁的有点也就一目了然了:所有的线程在CPU执行调度的时候,都能够得到资源,不至于有些线程因为抢占不到资源,而一直无法被执行。

缺点:

有优点就必然有缺点:既然可以有序的执行,那肯定就会降低吞吐量,队列里面除了第一个线程,其它线程将会全部阻塞,CPU唤醒阻塞线程的开销会很大。

0 2

非公平锁

何为非公平锁?同样字面意思理解,就是一种不公平机制的锁,随机抢占资源,不跟你整那一套先到先得的规则。线程加锁时先尝试获取锁,获取不到就自动到队尾等待。

上述同样的代码,把ReentrantLock的有参构造改成false,执行看下打印值:

非公平锁时随机抢占的机制,所以也会出现公平锁的情况,所以测试时候不用纠结这个。

优点

非公平的性能肯定是高于公平锁性能的,非公平锁能更充分的利用CPU的时间片,尽量的减少CPU空闲的状态时间。

缺点:

既然是随机抢占资源的,那就肯定会有队列中间的线程一直获取不到锁或长时间获取不到锁,导致“饿死”。

0 3

可重入锁

何为可重入锁:可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,但前提是同一个对象或同一个类中,这样的锁就叫可重入锁。

上面两种锁介绍的都是ReentrantLock实现的,而在真正的使用中保证线程安全synchronized也是不可缺少的,这两者都是可重入锁。

父方法和子方法同时输出了线程的名称,表明即使递归使用synchronized也没有发生死锁,证明其是可重入的。

那这两者有什么区别呢?

0 4

ReentrantLock和synchronized区别

1,synchronized依赖于JVM,ReentrantLock依赖于API,需要lock()和unlock()方法配合try/finally语句块来实现。

2,synchronized使用比较简单方便,并且有编译器去保证锁的加锁和释放,而ReentrantLock需要手动声明加锁和释放锁,因此ReentrantLock锁的粒度和灵活度要高于synchronized。

3,synchronized在优化以前,性能方面和ReentrantLock是差很多的,但是在JDK1.6之后synchronized引入了偏向锁和自旋锁,两者的性能就差不多了,官方甚至都建议使用synchronized。其实synchronized的优化感觉就是采用ReentrantLock的CAS技术(这里不要有歧义:ReentrantLock底层是基于AQS实现的,但AQS的本质其实就是volatile+CAS)。都是试图在用户态就把加锁的问题解决,避免进入内核态的线程阻塞。

0 5

读写锁

读写锁实际上就一种特殊的自旋锁,读写锁的规则可以共享读,但只能一个写,读读不互斥、读写互斥、写写互斥,而一般的独占锁是啥都互斥,而我们实际场景中,读一定是大于写的,一般情况下独占锁的效率低来源于高并发下对临界区的资源抢占导致线程上下文切换。因此当并发不是很高的情况下,读写锁需要额外的维护读锁的状态,可能还不如独占锁的效率高,所以要根据实际场景而定。(本篇不深入将方法的实现和源码)

0 6

synchronized JDK1.6优化之后的锁

6.1  偏向锁

当线程执行到临界区时,会利用CAS操作,将线程ID插入到Mark Word(对象头中的标记子),同时修改偏向锁的标志位。

所谓临界区,就是只允许一个线程进去执行操作的区域,即同步代码块,cas是一个原子性的操作。

这个锁会偏向于第一个获得它的线程,在接下来的执行过程中,假如该锁没有被其它线程所获取,没有其它线程来竞争该锁,那么持有偏向锁的线程将永远不需要进行同步操作。也就是说,在此线程之后的执行过程中,如果再次进入或者退出同一段同步块代码,并不再需要去进行加锁或者解锁操作。

6.2 锁膨胀

    综合偏向锁,当出现有两个线程来竞争的话,那么偏向锁就失效了,此时锁就会膨胀,升级为轻量锁,这就是我们经常说的锁膨胀。

6.3 锁撤销

当偏向锁失效,就得把该锁撤销,锁撤销的开销花费是很大的,大概的过程:

  1. 在一个安全点停止拥有锁的线程。

  2. 遍历线程栈,如果存在锁记录的话,需要修复锁记录和Markword,使其变成无锁状态。

  3. 唤醒当前线程,将当前锁升级成轻量级锁。所以,如果某些同步代码块大多数情况下都是有两个及以上的线程竞争的话,那么偏向锁就会是一种累赘,对于这种情况,我们可以一开始就把偏向锁这个默认功能给关闭

6.4 自旋锁(jdk1.4.2引入的)

    自旋锁属于轻量级锁。所谓自旋,就是指当有另外一个线程来竞争锁时,这个线程会在原地循环等待,而不是把该线程阻塞,直到那个获得锁线程释放锁之后,这个线程就可以马上获取到锁。

注意:锁在原地循环的时候,是会消耗CPU的,就相当于在循环一个啥也没有的for循环。所以,轻量级锁适用于那些同步代码块执行的很快的场景,这样,线程原地等待很短很短的时间就能够获得锁了。

经验表明,大部分同步代码块执行的时间都是很短很短的,也正是基于这个原因,才有了轻量级锁这么个东西。

自旋锁存在的问题:

1,如果同步代码块执行的很慢,需要消耗大量的时间,这个时候,其它线程在原地等待空消耗的CPU,这点就会有问题。

2,本来一个线程把锁释放之后,当前线程是能够获得锁的,但是假如这个时候好几个线程都在竞争这个锁的话,那么可能当前线程会获取不到锁,还在原地”转圈圈“,转到怀疑人生。

那基于这个问题,我们就需要给线程空循环设置一个值,当线程超过了这个次数,我们就认为继续使用自旋锁就不合适了,一直原地空循环,这谁受得了。此时锁就会再次膨胀,升级为重量级锁。

默认情况下,自旋的次数为10次,当然可以通过-XX:PreBlockSpin来进行更改。

6.5 自适应自旋锁

    所谓自适应自旋锁就是线程空循环等待的自旋次数并非是固定的,而是会动态着根据实际情况来改变自旋等待的次数。
其大概原理是这样的:
    假如一个线程1刚刚成功获得一个锁,当它把锁释放了之后,线程2获得该锁,并且线程2在运行的过程中,此时线程1又想来获得该锁了,但线程2还没有释放该锁,所以线程1只能自旋等待,但是虚拟机认为,由于线程1刚刚获得过该锁,那么虚拟机觉得线程1这次自旋也是很有可能能够再次成功获得该锁的,所以会延长线程1自旋的次数。
另外,如果对于某一个锁,一个线程自旋之后,很少成功获得该锁,那么以后这个线程要获取该锁时,是有可能直接忽略掉自旋过程,直接升级为重量级锁的,以免空循环等待浪费资源。

轻量级锁也被称为非阻塞同步、乐观锁,因为这个过程并没有把线程阻塞挂起,而是让线程空循环等待,串行执行。

本篇着重讲的是一下名词的概念,之后会对于ReentrantLock的源码解析来真正的理解这些锁的底层实现原理,因为知其然先知其所以然啦。

- End -

java 锁_Java之线程并发的各种锁、锁、锁相关推荐

  1. Java5线程并发库之LOCK(锁)CONDITION(条件)实现线程同步通信

    为什么80%的码农都做不了架构师?>>>    Lock(锁)&Condition(条件)实现线程同步通信 接下来介绍,java5线程并发库里面的锁.跟锁有关的类和接口主要是 ...

  2. java mysql 行锁_Java如何实现对Mysql数据库的行锁?

    行锁 mysql实现行级锁的两大前提就是,innodb引擎并且开启事务.由于MySQL/InnoDB的加锁分析,一般日常中使用方式为: select .... from table where ... ...

  3. java释放锁_java – 一个线程在完成后释放锁吗?

    简单测试可能会显示在线程终止时未释放锁定: import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock ...

  4. java 堆栈_Java中线程与堆栈的关系

    栈是线程私有的,每个线程都是自己的栈,每个线程中的每个方法在执行的同时会创建一个栈帧用于存局部变量表.操作数栈.动态链接.方法返回地址等信息.每一个方法从调用到执行完毕的过程,就对应着一个栈帧在虚拟机 ...

  5. Java并发编程学习四:锁

    一.锁的分类 Java中有着各种各样的锁,对于锁的分类也是多种多样,一把锁可能同时占有多个标准,符合多种分类. 对锁的常见分类有以下几个标准: 1. 偏向锁/轻量级锁/重量级锁 这三种锁特指 sync ...

  6. java同步与死锁_Java多线程 - 线程同步与死锁

    一.线程同步 1)模拟多个用户同时从银行账户里面取钱 ● Account 类:银行账户类,里面有一些账户的基本信息,以及操作账户信息的方法 //模拟银行账户 classAccount {private ...

  7. java 锁_Java 锁之我见

    今天我们来聊聊 Java 里面的各种锁:偏向锁.轻量级锁.重量级锁,以及三个锁之间是如何进行锁膨胀的. 众所周知,线程阻塞带来的上下文切换的代价是很大的,Java 为了尽量减少上下文的切换从而引入了更 ...

  8. java重入锁 自旋锁_java 自旋锁(可重入且无死锁)

    java自旋锁 的实现原理:如果自旋锁被另外一个线程对象持有,那么当前获取锁的线程将陷入while循环等待,直到那个持有自旋锁的线程对象释放它所持有的自旋锁,那么那些想要获取该自旋锁的线程对象 将会有 ...

  9. java线程安全的方法_Java实现线程安全的方式

    多线程环境中如何保证线程安全?java可以实现线程安全的方式归纳如下: 1.使用synchronized关键字 synchronized关键字可以修饰方法和代码块,它的语义是保证同一段代码同一时间只能 ...

最新文章

  1. 棋盘格氧化铝标定板漫反射不反光12*9方格视觉光学校正板
  2. SAP Spartacus table里显示较长数据时自动显示省略号的设置
  3. P6478-[NOI Online #2 提高组]游戏【dp,二项式反演】
  4. 广州驾考科目三电子考16日全面启动
  5. 前两年在MSDN里找到的HTC示例,一直没用过,先在这里存个备份
  6. uc3842开关电源电路图_详解6款简单的开关电源电路设计原理图
  7. 吴恩达机器学习笔记 1单变量线性回归
  8. 基于Emgu cv的图像拼接(转)
  9. ubuntu 16.04 更改jupyter notebook工作路径
  10. Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition(SPP-net)
  11. google搜索从入门到精通。
  12. mysql增加约束sql语句_sql语句添加约束
  13. 嵌入式软件工程师就只需会写C代码吗
  14. B3610 [图论与代数结构 801] 无向图的块
  15. wfGo 围棋 联机对战模式
  16. JZOJ_2499_东风谷早苗 (Standard IO)
  17. 班级校园网页设计作业 静态HTML我的班级网页 DW班级网站模板下载 大学生简单班级网页作品代码 我的大学网页制作 学生班级网页设计作业
  18. OpenCV-DoG
  19. rx6800s什么水平N卡 rx6800s什么水平
  20. oracle甲骨文账号-用于下载oracle JDK

热门文章

  1. 您是否尝试过MicroProfile Starter?
  2. log4j性能 slf4j_Log4j 2:性能接近疯狂
  3. java 垃圾回收手动回收_Java垃圾回收(2)
  4. 在Amazon Elastic Beanstalk上部署Spring Boot应用程序
  5. Spring Bootstrap中带有配置元数据的高级配置
  6. jsf集成spring_Spring和JSF集成:MVC螺母和螺栓
  7. 我的Dojo中有一个Mojo(如何编写Maven插件)
  8. Nifty JUnit:在方法和类级别上使用规则
  9. 如何安全使用SWT的显示器asyncExec
  10. ActiveMQ –经纪人网络解释–第4部分