锁一个整篇

乐观锁VS悲观锁

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁

乐观锁:假设数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做。

 synchronized初始使用乐观锁策略。当发现锁竞争比较频繁的时候,就会自动切换成悲观锁策略

读写锁

 多线程之间,数据的读取方之间不会产生线程安全问题,但数据的写入方互相之间以及和读者之间都需要进行互斥。如果两种场景下都用同一个锁,就会产生极大的性能损耗。所以读写锁因此而产生。读写锁,在执行加锁操作时需要额外表明读写意图,复数读者之间并不互斥,而写者则要求与任何人互斥。

synchronized不是读写锁

重量级锁VS轻量级锁

synchronized开始是轻量级锁,如果锁冲突比较严重,就会变成重量级锁

自旋锁(挂起等待锁)

  线程在抢锁失败后进入阻塞状态,放弃CPU,需要过很久才能再次被调度

 如果获取锁失败,立即再尝试,无限循环,直到获取锁为止,第一次获取锁失败,第二次的尝试会在极短的时间内到来

公平锁VS非公平锁

 

 操作系统内部的线程调度就可以视为是随机的。如果不做限制,锁就是非公平锁。如果要实现公平锁,就需要依赖额外的数据结构。来记录线程们的先后顺序

 synchronized是非公平锁

可重入锁VS不可重入锁

可重入锁:允许同一个线程多次获取同一把锁

(比如一个递归函数里有加锁操作,递归过程中锁不会阻塞自己,即为可重入锁)

Synchronized是可重入锁 

 

 

 面试题1》讲解一下你自己理解的CAS机制

 全称Compare and swap,既“比较并交换”。相当于通过一个原子的操作,同时完成“读取内存,比较是否相等,修改内存”这三个步骤

2》ABA问题怎么解决?

给要修改的数据引入版本号,在CAS比较数据当前值和旧值的同时,也要比较版本号是否符合预期。如果当前版本号和之前读到的版本号一致,就真正执行修改操作,并让版本号自增;如果发现当前版本号比之前读到的版本号大,就认为操作失败

Synchronized原理

基本特性:1.开始时是乐观锁。如果锁冲突频繁,就转换为悲观锁

2.开始是轻量级锁实现,如果锁被持有的时间较长,就换成重量级锁

3.实现轻量级锁的时候大概率用到自旋锁策略

4.是不公平锁,可重入锁,不是读写锁

1》什么是偏向锁?

偏向锁不是真的加锁,而只是在锁的对象头中记录一个标记(记录该锁所属的线程)。如果没有其他线程参与竞争锁,那么就不会真正执行加锁操作,从而降低程序开销。一旦真的涉及到其他的线程竞争,再取消偏向锁状态,进入轻量级锁状态

介绍下Callable是什么?

Callable是一个interface.相当于把线程封装了一个“返回值”。方便开发者借助多线程的方式计算结果

Callable和Runnable相对,都是描述一个“任务”,Callable描述的是带有返回值的任务,Runnable描述的是不带返回值的任务

Callable通常需要搭配FutureTask来使用。FutureTask用来保存Callable的返回结果,因为Callable

往往是另一个线程中执行的,啥时候执行完并不确定。FutureTask就可以负责这个等待结果出来的工作

 

 面试:1》线程同步的方式有哪些?

synchronized,ReentrantLock,Semaphore等都可以用于线程同步

2》为什么有了synchronized还需要juc下的lock?

以juc的ReentrantLock为例

      synchronized使用时不需要手动释放锁。ReentrantLock使用时需要手动释放。使用起来更灵活;synchronized在申请锁失败时,会死等。ReentrantLock可以通过tyrlock的方式等待一段时间就放弃;synchronized是非公平锁,ReentrantLock默认是非公平锁。可以通过构造方法传入一个true开启公平锁模式;synchronized是通过Object的wait/notify实现等待/唤醒。每次唤醒的是一个随即等待的线程。ReentrantLock搭配Condation类实现等待-唤醒,可以更精确控制唤醒某个指定的线程

      

 

 

面试:1》ConcurrentHashMap的读是否要加锁,为什么?

读操作没有加锁。目的是为了进一步降低锁冲突的概率。为了保证读到刚修改的数据,搭配了volatile关键字

2》介绍下ConcurrentHashMap的锁分段技术?

简单说就是把若干个哈希桶分成一个“段”,针对每个段分别加锁。目的也是为了降低锁竞争的概率,当两个线程访问的数据恰好在同一个段上的时候,才触发锁竞争

3》Hashtable和HashMap、ConcurrentHashMap之间的区别?

  HashMap:线程不安全。Key允许为null

  Hashtable:线程安全。使用synchronized锁Hashtable对象,效率较低,key不允许为null

  ConcurrentHashMap:线程安全。使用synchronized锁每个链表头结点,锁冲突概率低,充分利用CAS机制。优化了扩容方式。Key不允许为null

死锁:多个线程同时被阻塞,他们中的一个或者全部都在等待某个资源被释放,由于线程被无限期地阻塞,因此程序不可能正常终止

如何避免死锁:

1.互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用

2.不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放

3.请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有

4.循环等待,即存在一个等待队列; 

面试:1》谈谈volatile关键字的用法?

volatile能够保证内存可见性。强制从主内存中读取数据,此时如果有其他线程修改被volatile修饰的变量,可以第一时间读取到最新的值

2》Java多线程是如何实现数据共享的?

JVM把内存分成了这几个区域:

方法区、堆区、栈区、程序计数器。

其中堆区这个内存区域是多个线程之间共享的

只要把某个数据放到堆内存中,就可以让多个线程都能访问到

3》Java创建线程池的接口是什么?

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值