一.什么是死锁:
死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
二.产生死锁的四个必要条件:
1.资源互斥:对所分配的资源进行排它性控制,锁在同一时刻只能被一个线程使用。
2.不可剥夺:线程已获得的资源在未使用完之前,不能被剥夺,只能等待占有者自行释放锁。
3.请求等待:当线程因请求资源而阻塞时,对已获得的资源保持不放
4.循环等待:线程之间的相互等待
三.ReentrantLock 与 synchronized有什么区别?
ReentrantLock | Synchronized | |
锁获得机制 | AQS | 监视器Monitor |
获得锁 | 可以通过tryLock()尝试获得锁,更灵活 | 线程抢占模型 |
释放锁 | 必须通过unlock()释放锁 | 自动释放 |
锁类型 | 支持公平锁和非公平锁 | 非公平锁 |
可重入性 | 可重入 | 可重入 |
四.sleep()和wait()有什么区别?
两者最主要的区别在于:sleep方法没有释放锁,而wait方法释放了锁。
两者都可以暂停线程的执行。
wait()通常被用于线程交互/通信,sleep()通常被用于暂停执行。
wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll方法,sleep方法执行完成后,线程会自动苏醒。或者可以使用wait(long timeout)超时后线程会自动苏醒。
五.乐观锁和悲观锁是什么?
悲观锁(Pessimistic Locking),具有强烈的独占和排他特性。它指的是对数据被外界修改持保守态度。因此,在整个执行过程中,将处于锁定状态,所以,悲观锁是一种悲观思想,它总认为最坏的情况可能会出现,它认为数据很有可能会被其他人所修改,所以悲观锁在持有数据的时候总会把资源或者数据锁住,这样其他线程要想请求这个资源的时候就会阻塞,直到等到悲观锁把资源释放为止。Java中的Synchronized和ReentrantLock是一种悲观锁的实现。因为Synchronized和ReetrantLock不管是否持有资源,它都会尝试去加锁。
乐观锁(Optimistic Locking)相对悲观锁而言,乐观锁采取了更加宽松的加锁机制。乐观锁的思想与悲观锁的思想相反,它总认为资源和数据不会被别人修改,所以读取不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过。Java中的StampedLock和AtomicInteger是一种乐观锁思想的实现。
六.详细说一下sychronized如何实现的?锁升级的过程了解吗?
Synchronized同步锁,使用Synchronized关键字将一段代码逻辑,用一把锁给锁起来,只有获得了这把锁的线程才访问。并且同一时刻,只有一个线程能持有这把锁,这样就保证了同一时刻只有一个线程能执行被锁住的代码,从而确保代码的线程安全。
用法:修饰实例方法:默认为this当前方法调用对象
修饰静态方法:默认为Class对象
修饰代码块:是某个指定Java对象。
synchronized代码块是由一对monitorenter/monitorexit指令实现。synchrized是通过对象内部的叫做监视器(monitor)来实现的,线程通过执行monitotenter指令尝试获取monitor的所有权。当monitor被占用时就会处于锁定状态。
如果monitor的进入数为0,则该进程进入monitot,然后将进入数设置为1,该线程即为monitor的所有者,代表持有锁。
2.如果线程已经占有该monitot,只是重新进入,则进入monitot的进入数加1;
3.如果其他线程已经占用了monitor,则该进程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
偏向锁是在单线程情况下自动获得的锁,如果发现有多于一个线程调用,再升级为轻量级锁,实现原理:JVM会利用CAS操作,在对象头上的Mark Word部分设置线程ID。
轻量级锁适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一把锁的情况,就会导致轻量级锁升级为重量级锁。原理:在升级成轻量级锁的过程中,JVM虚拟机会在当前线程的栈帧中建立一个Lock Record锁记录,存放当前对象锁的Mark Word的拷贝,拷贝成功后,会将当前对象所的Mark Word中的ptr_to_lock_record更新为指向Lock Record的指针,并将Lock Record里的owner指针指向到对象的Mark Word。如果更新成功,代表升级成轻量级锁,并将对象Mark Word的lock标志位设置为00.如果失败,检查对象的Mark Word是否已经指向当前线程的栈帧,如果指向,则继续执行,否则,标志位变成10,升级成重量级锁,其他等待线程阻塞,当前线程自旋尝试获取锁。