面试题-13锁

首先来说下synchronize和Lock的区别

两者都是锁,用来控制并发冲突,区别在于Lock是个接口,提供的功能更加丰富,除了这个外,他们还有如下区别:
synchronize自动释放锁,而Lock必须手动释放,并且代码中出现异常会导致unlock代码不执行,所以Lock一般在Finally中释放,而synchronize释放锁是由JVM自动执行的。
Lock有共享锁的概念,所以可以设置读写锁提高效率,synchronize不能。(两者都可重入)
Lock可以让线程在获取锁的过程中响应中断,而synchronize不会,线程会一直等待下去。lock.lockInterruptibly()方法会优先响应中断,而不是像lock一样优先去获取锁。
Lock锁的是代码块,synchronize还能锁方法和类。

二、synconized实现原理

1、jvm基于进入和退出Monitor对象来实现方法同步和代码块同步。
方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。JVM可以从方法常量池中的方法表结构(method_info
Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的
ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词),
然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。
2.代码块的同步是利用monitorenter和monitorexit这两个字节码指令。它们分别位于同步代码块的开始和结束位置。当jvm执行到monitorenter指令时,当前线程试图获取monitor对象的所有权,如果未加锁或者已经被当前线程所持有,就把锁的计数器+1;当执行monitorexit指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。如果获取monitor对象失败,该线程则会进入阻塞状态,直到其他线程释放锁。
各种锁的理解

1、公平锁、非公平锁 公平锁: 非常公平, 不能够插队,必须先来后到! 非公平锁:非常不公平,可以插队 (默认都是非公平) 2、可重入锁
可重入锁(递归锁)
在这里插入图片描述

Synchronized
package com.kuang.lock;
import javax.sound.midi.Soundbank;
// Synchronized
public class Demo01 {
    
    
public static void main(String[] args) {
    
    
Phone phone = new Phone();
new Thread(()->{
    
    
phone.sms();
},"A").start();
new Thread(()->{
    
    
phone.sms();
},"B").start();
}
}
class Phone{
    
    
public synchronized void sms(){
    
    
System.out.println(Thread.currentThread().getName() + "sms");
call(); // 这里也有锁
}
public synchronized void call(){
    
    
System.out.println(Thread.currentThread().getName() + "call");
}
}
Lock 版
package com.kuang.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
    
    
public static void main(String[] args) {
    
    
Phone2 phone = new Phone2();
new Thread(()->{
    
    
phone.sms();
},"A").start();
new Thread(()->{
    
    
phone.sms();
},"B").start();
}
}
class Phone2{
    
    
Lock lock = new ReentrantLock();
public void sms(){
    
    
lock.lock(); // 细节问题:lock.lock(); lock.unlock(); // lock 锁必须配对,否
则就会死在里面
lock.lock();
try {
    
    
System.out.println(Thread.currentThread().getName() + "sms");
call(); // 这里也有锁
} catch (Exception e) {
    
    
e.printStackTrace();
} finally {
    
    
lock.unlock();
lock.unlock();
}
}
public void call(){
    
    
lock.lock();
try {
    
    
System.out.println(Thread.currentThread().getName() + "call");
} catch (Exception e) {
    
    
e.printStackTrace();
} finally {
    
    
lock.unlock();
}
}
}

3、自旋锁
spinlock
在这里插入图片描述

我们来自定义一个锁测试
测试

package com.kuang.lock;
import java.util.concurrent.atomic.AtomicReference;
* 自旋锁
*/
public class SpinlockDemo {
    
    
// int 0
// Thread null
AtomicReference<Thread> atomicReference = new AtomicReference<>();

// 加锁
public void myLock() {
    
    
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "==> mylock");
// 自旋锁
while (!atomicReference.compareAndSet(null, thread)) {
    
    
}
}

// 解锁
// 加锁
public void myUnLock() {
    
    
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "==> myUnlock");
atomicReference.compareAndSet(thread, null);
}
}
/**
* 锁
*/
public class asdf {
    
    
public static void main(String[] args) {
    
    
//如果用的是同一个锁 谁先获得锁 谁先用 其他线程只能等待获得锁才能向下走
Lock lock = new ReentrantLock();
// Lock lock1 = new ReentrantLock();
new Thread(()->{
    
    
lock.lock();
try {
    
    
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName());
} catch (Exception e) {
    
    
e.printStackTrace();
} finally {
    
    
lock.unlock();
System.out.println("22");
}

},"t1").start();

new Thread(()->{
    
    
lock.lock();
try {
    
    
System.out.println(Thread.currentThread().getName());
} catch (Exception e) {
    
    
e.printStackTrace();
} finally {
    
    
lock.unlock();
System.out.println("33");
}

},"t2").start();
}
}
//运行结果
//t1
//t2
//33
//22

4、死锁
在这里插入图片描述

死锁测试,怎么排除死锁:
public class test1 {
    
    
public static void main(String[] args) {
    
    
String lockA = "A锁";
String lockB = "B锁";
new Thread(new MyThread(lockA,lockB)).start();
new Thread(new MyThread(lockB,lockA)).start();
}
}

class MyThread implements Runnable{
    
    
private String lockA;
private String lockB;

public MyThread(String lockA, String lockB) {
    
    
this.lockA = lockA;
this.lockB = lockB;
}

@Override
public void run() {
    
    
synchronized (lockA){
    
    
System.out.println(Thread.currentThread().getName()+lockA+"get->"+lockB);
try {
    
    
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
    
    
e.printStackTrace();
}
synchronized (lockB){
    
    
System.out.println(Thread.currentThread().getName()+lockB+"get->"+lockA);
}
}
}
}

解决问题 1、使用jps -l定位进程号

2、使用jstack +进程号 找到死锁问题

面试,工作中! 排查问题: 1、日志 9 2、堆栈 1

猜你喜欢

转载自blog.csdn.net/zyf_fly66/article/details/114085724