面试

android 多线程 — 锁基础

2019-04-17  本文已影响0人  前行的乌龟

ps:这连天看锁看了好多资料,资料也不分前后顺序,理顺概念,搞定关联脉络着是废了一番劲,总算是基本搞清楚了,真是不容易啊,这个时刻我想起一句话:越往深里学,越得看书,权威书籍的资料更全面,连贯

老规矩,妹子镇楼,抚慰心灵



锁涉及到的点

锁涉及到的点很多,这里从底层向上列举出来:


AQS(抽象队列同步器)、非阻塞数据结构和原子变量类等基础类都是基于 volatile 变量的读/写和 CAS 实现,是 Java 并发包里实现锁、同步的一个重要的基础框架,而像 Lock 、同步器、阻塞队列、Executor 和并发容器等高层类又是基于基础类实现

其实每个部分都可以讲很多,这里列出来,大家心里有个数


java 线程阻塞的代价


Synchronize 原理

简单来说 Synchronize 是通过 JVM 的对象监视器 Monitor 进入和退出命令来实现对方法、同步块的同步的,在进入同步方法调用前加入一个 monitor.enter 指令,在退出方法和异常处插入 monitor.exit 的指令,本质就是获取一个 Monitor,而这个获取过程具有排他性从而达到了同一时刻只能一个线程访问的目的,而对于没有获取到锁的线程将会阻塞到方法入口处,直到获取锁的线程 monitor.exit 之后才能尝试继续获取锁。

然后我们来看看线程在进入同步队列之后是怎么竞争资源的


别的不用关系,我们熟悉 Contention List 、EntryList 就行

竞争顺序:

其他特征:


锁的特性

这里介绍 java 中并发核心锁的特性,不是具体的锁,具体的锁一般都是多种特性符合存在的,但是的确有些锁只具有一种特性,但是这种锁一般都是有专门应用场景的,不具有普遍适用性


乐观锁 / 悲观锁


公平锁 / 非公平锁


独享锁 / 共享锁


分段锁


可重入锁


自旋锁

自旋锁是互斥锁的进步,自旋锁在有其他线程竞争同步代码时,我们可以让后面请求锁的那个线程“稍等一会”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁,为了让线程等待,我们只须让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。这种优化思路就是基于前面说的锁阻塞的时间总是很短的考量

自旋锁在 JDK 1.4 中就已经引入,只不过默认是关闭的,在 JDK 1.6 中就已经改为默认开启了,但是自旋锁也有自身的局限


java 中锁的 API

上述两种锁机制类型都是“互斥锁”,学过操作系统的都知道,互斥是进程同步关系的一种特殊情况,相当于只存在一个临界资源,因此同时最多只能给一个线程提供服务。但是,在实际复杂的多线程应用程序中,可能存在多个临界资源,这时候我们可以借助Semaphore信号量来完成多个临界资源的访问。

Semaphore基本能完成ReentrantLock的所有工作,使用方法也与之类似,通过acquire()与release()方法来获得和释放临界资源。

经实测,Semaphone.acquire()方法默认为可响应中断锁,与ReentrantLock.lockInterruptibly()作用效果一致,也就是说在等待临界资源的过程中可以被Thread.interrupt()方法中断。

此外,Semaphore也实现了可轮询的锁请求与定时锁的功能,除了方法名tryAcquire与tryLock不同,其使用方法与ReentrantLock几乎一致。Semaphore也提供了公平与非公平锁的机制,也可在构造函数中进行设定。

Semaphore的锁释放操作也由手动进行,因此与ReentrantLock一样,为避免线程因抛出异常而无法正常释放锁的情况发生,释放锁的操作也必须在finally代码块中完成

性能对比:
锁使用策略:

一般来说同步我们优先考虑 synchronized ,synchronized 是 JVM 层面的,自动加锁取消锁,不会出 bug,如果有特殊需要再进一步优化。ReentrantLock 和 Atomic 如果用的不好,不仅不能提高性能,还可能带来灾难



参考资料:

上一篇 下一篇

猜你喜欢

热点阅读