1.乐观锁和乐观锁

两者本来是数据库中的概念,但Java锁中也有相似的思维。

乐观锁:认为数据在个别状况下不会造成抵触,在拜访记录前不会加排他锁,而是在进行数据提交更新时,才会对数据抵触与否进行检测

乐观锁:认为数据很容易被其余线程批改,在解决数据前就加锁,并在整个数据处理过程中数据都处于锁定状态

2.偏心锁、非偏心锁

偏心锁:依据线程申请锁的程序来获取锁
非偏心锁:抢占式获取锁

3.什么是死锁

具备以下4个条件就会产生死锁:

  • 互斥条件:指线程对曾经获取到的资源进行排它性应用,即该资源同时只由一个线程占用*。如果此时还有其余线程申请获取该资源,则请求者只能期待,直至占有资源的线程开释该资源
  • 申请并持有条件:指一个线程曾经持有了至多一个资源,但又提出了新的资源申请,而新的资源已被其余线程占有,所以以后线程会被阻塞,但阻塞的同时并不开释本人曾经获取的资源
  • 不可剥夺条件:指线程获取到的资源在本人应用完之前不能被其余线程抢占,只有在本人应用结束后才由本人开释该资源
  • 循环期待:产生死锁的时候,必然存在一个(线程-资源)的环形链,即线程一在期待线程二占用的资源,线程二在期待线程三期待的资源...

例如:

public class DeadLock {    private static Object obj = new Object();    private static Object obj2 = new Object();    public static void main(String[] args) throws InterruptedException {        Thread t = new Thread(() -> {            System.out.println("线程1,开始获取obj1锁");            synchronized (obj) {                System.out.println("线程1,获取obj1锁胜利");                System.out.println("线程1,开始获取obj2锁");                //休眠让两个线程都获取到对应的锁                sleep(1);                synchronized (obj2) {                    System.out.println("线程1,获取obj2锁胜利");                }            }        });        t.start();        Thread t2 = new Thread(() -> {            System.out.println("线程2,开始获取obj2锁");            synchronized (obj2) {                System.out.println("线程2,获取obj2锁胜利");                System.out.println("线程2,开始获取obj1锁");                sleep(1);                synchronized (obj) {                    System.out.println("线程2,获取obj1锁胜利");                }            }        });        t2.start();        t2.join();    }    public static void sleep(long second){        try {            Thread.sleep(second * 1000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

4.如何防止死锁

  1. 使线程依照指定的程序获取锁,并开释锁

    Thread 1:  lock A   lock BThread 2:   wait for A   lock C (when A locked)Thread 3:   wait for A   wait for B   wait for C
  2. 设置获取锁超时工夫,超过工夫,主动放弃获取锁,并开释曾经持有的锁,应用lock.tryLock(timeount)代替synchronized
  3. 防止一个线程同时获取多个锁

5.什么是活锁

活锁也是一种死锁,死锁的话,所有线程都处于阻塞状态,活锁是因为某些条件没有满足,导致始终反复尝试,但有可能自行解开,比方设置了重试次数限度,或者超时工夫

6.sleep 、wait、yield的区别

sleep:

  • 让以后线程休眠指定工夫
  • 不开释锁资源
  • 可通过调用interrupt()办法来唤醒休眠线程

wait:

  • 让以后线程进入期待状态,当其余线程调用notify或者notifyAll办法时,以后线程进入就绪状态
  • 以后线程会开释已获取的锁资源,并进入期待队列
  • 只能在synchronized中应用

yield:

  • 让出线程以后的CPU执行工夫,以后线程进入就绪状态,不阻塞以后线程,只是让同优先级或者更高优先级的线程优先执行
  • 线程下次调度时仍旧有可能执行到

7.什么是虚伪唤醒?如何防止

https://blog.csdn.net/LuckyBu...

wait(),notify()源码剖析:https://www.jianshu.com/p/f44...

8.Synchronized原理

Synchronized能够润饰一般办法、同步办法块、静态方法;

  • 一般办法锁是以后实例对象
  • 静态方法锁是以后类的Class对象
  • 同步办法块锁是Synchonized配置的对象;
    用的锁是存在对象头里的,依据mark word的锁状态来判断锁,如果锁只被同一个线程持有应用的是偏差锁,不同线程相互交替持有锁应用轻量级锁,多线程竞争应用重量级锁。锁会按偏差锁(单线程持有)->轻量级锁(两线程竞争)->重量级锁(多线程竞争)降级,称为锁收缩
    https://github.com/farmerjohn...

9.synchronized和Lock的区别

  1. synchronized 是Java内置关键字,Lock是Java类
  2. synchronized 无奈显式的判断是否获取锁的状态,Lock能够判断是否获取到锁
  3. synchronized 会主动开释锁,Lock须要在finally中手工开释锁
  4. synchronized 不同线程获取锁只有一个线程能获取胜利,其余线程会始终阻塞直到获取锁,Lock有阻塞锁,也有非阻塞锁,阻塞锁还有尝试设置,性能更强
  5. synchronized 可重入,不可中断,非偏心,Lock锁可重入,可判断,有偏心锁,非偏心锁
  6. Lock锁适宜大量同步代码的同步问题,synchronized锁适宜代码大量的同步问题

10.synchronized 可重入是怎么实现的

可重入是指:当一个线程持有一个锁对象之后,再次去获取同一个锁时可能胜利获取

因为synchronized应用的是锁对象,当某个线程第一次持有锁后,会批改锁对象的mark word锁状态为偏差锁,偏差锁会在以后线程的栈帧中建设一个锁记录空间,mark word会将指针指向栈中的锁记录。当线程再次获取锁对象的时候,会查看mark word 中的指针是否指向以后线程的栈帧,如果是就间接获取锁,如果不是就须要竞争

11.ReentrantLock可重入性怎么实现的?

因为ReentrantLock是通过AQS来实现的,其应用了AQS的state状态值来示意线程获取该锁的可重入次数,默认状况下state为0示意以后锁没有被任何线程持有,当一个线程获取该锁时会尝试应用CAS设置state值为1,如果CAS设置胜利则以后线程获取了该锁,而后记录该锁的持有者为以后线程,在该线程没有开释锁的状况下第二次获取该锁后,状态值被设置2,这就是能够重入次数,在开释锁的时候,须要通过CAS将状态值减1,直到状态值为0,示意以后线程开释该锁

12.AbstractQueuedSynchronizer的作用

形象同步队列简称AQS,是实现同步器的根底组件,并发包中的锁都是基于其实现的,要害是先进先出的队列,state状态,并且定义了 ConditionObject ,领有两种线程模式,独占模式和共享模式

  • AQS核心思想

    如果被申请的共享资源闲暇,则将以后申请资源的线程设置为无效的工作线程,并且将共享资源设置为锁定状态。如果被申请的共享资源被占用,那么就须要一套线程阻塞期待以及被唤醒时锁调配的机制,这个机制应用CLH队列实现的,行将临时获取不到锁的线程退出到队列中

    CLH(Craig,Landin,and Hagersten)队列是一个虚构的双向队列(虚构的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条申请共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的调配, 并放弃了高低节点,以后申请资源的线程

13.非偏心锁和偏心锁在ReetrantLock里的实现过程是怎么的

如果一个锁是偏心的,那么锁的获取程序就应该合乎申请的相对工夫程序,FIFO。

对于非偏心锁,只有CAS设置同步状态胜利,则示意以后线程获取了锁,而偏心锁还须要判断以后节点是否有前驱节点,如果有,则示意有线程比以后线程更早申请获取锁,因而须要期待前驱线程获取并开释锁之后能力持续获取锁。

14.自旋锁、自适应自旋、锁打消、锁粗化、轻量级锁、偏差锁、重量级锁概念

自旋锁:开启线程执行一个忙循环,直到须要更新的值为期待值为止
自适应自旋:自旋工夫不再固定,由前一次在同一个锁上的自旋工夫及锁的拥有者状态来决定,比方在同一个锁对象上,自旋期待刚刚胜利取得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次胜利,进而它将自旋期待更长时间,以冀望胜利获取锁,如果很少胜利取得过锁,那很可能会疏忽掉自旋过程,以防止CPU资源节约。
锁打消:JIT在运行时,对一些代码上要求同步,然而被检测到不可能存在共享数据竞争的锁进行打消
锁粗化:如果虚拟机探测到有一串系统的操作都对同一个对象加锁,将会把加锁同步的范畴扩大到整个序列的内部
轻量级锁:加锁是通过同步对象的对象头进行操作的,首先会在以后线程的栈帧中建设一个名为锁记录的空间,存储锁对象目前的Mark Word拷贝,会加Displaced前缀,而后通过CAS尝试将对象的Mark Word更新为指向Lock Record的指针,如果胜利,就取得了该对象的锁,如果失败,会查看Mark Word是否指向以后线程的栈帧,如果是就阐明曾经取得了锁,如果没有就阐明有其余线程抢占,轻量锁就会收缩成重量级锁;解锁也是通过CAS来操作,就是将Mark Word 替换为原来的值
偏差锁:锁偏差于第一个取得它的线程,如果在接下来的执行过程中,该锁没有被其余的线程获取,则持有偏差锁的线程将永远不须要再同步。-XX:+UseBiasedLocking 开启偏差锁

重量级锁:也叫互斥锁,一种乐观锁,会阻塞线程,通过对象外部的monitor锁来实现,monitor锁依赖底层操作系统的MutexLock互斥锁来实现

15.JDK8新增的锁

StampedLock 提供了三种模式的读写管制,当调用获取锁的系列函数时,会返回一个long型变量,反对在肯定条件下三种模式的互相转换

写锁writeLock: 一个排它锁或者独占锁,并且写锁不可重入

乐观读锁readLock: 共享锁,在没有线程独占获取写锁的状况下,多个线程能够同时获
取该锁,如果曾经有其余线程持有写锁,则其余线程申请读锁会被阻塞

乐观读锁tryOptimisticRead: 在操作数据前并没有通过CAS设置锁的状态,仅通过位运算测试