大厂面试---Redis面试题(含答案,相关知识点,面试考察次数统计)_安吉_lh1029的博客-CSDN博客
大厂面试问答题汇总分析---数据库(索引-聚集/非聚集,事务,mySql, 锁)_安吉_lh1029的博客-CSDN博客
大厂面试问答题汇总分析---网络安全类问题_安吉_lh1029的博客-CSDN博客
大厂面试问答题汇总分析--- 秒杀 / 限流 / 高并发_安吉_lh1029的博客-CSDN博客
【锁】的相关概念汇总_安吉_lh1029的博客-CSDN博客
大厂面试问答题汇总分析--- 多线程问题_安吉_lh1029的博客-CSDN博客
锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现。如 synchronized 和 ReentrantLock(并发包中的锁类)等 。
1、死锁
线程之间相互等着对方释放资源,而自己的资源又不释放给别人,这种情况就是死锁。所以,只要其中一线程释放了资源,死锁就会被解除。
1-1、死锁的4个条件:
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免:
1)尽量避免使用多个锁,并且只有需要时才持有锁定位死锁,嵌套的 synchronized 或者 lock 非常容易出问题。
2)如果必须使用多个锁,尽量设计好锁的获取顺序
3)使用带超时的方法,为程序带来更多可控性。Object.wait(…) 或者 CountDownLatch.await(…),都支持所谓的 timed_wait。
4)尽量不要几个功能用同一把锁。
1-2、写一个死锁的案例
public class Deadlock{
public static void main(String[] args){
Test thread1 = new Test(1,"线程1");
Test thread2 = new Test(2,"线程2");
thread1.start();
thread2.start();
}
}
class Test extends Thread{
//需要的资源只有一份,用static来保证只有一份
static Object lockA = new Object();
static Object lockB = new Object();
//变量,便于打印输出
private int flag;
private String threadName;
Test(int value, String name){
this.flag = value;
this.threadName = name;
}
@Override
public void run(){
System.out.println("flag ="+ flag);
if(flag == 1){
synchronized(lockA){
System.out.println(threadName +"获得lockA的锁");
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lockB){
System.out.println(threadName +"获得lockB的锁");
}
}
}else{
synchronized(lockB){
System.out.println(threadName +"获得lockB的锁");
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lockA){
System.out.println(threadName + "获得 lockA的锁");
}
}
}
}
}
上面这个案例死锁原因:
thread1在获取lockA的同时还想获取lockB
thread2在获取lockB的同时还想获取lockA
因此造成死锁,输出内容为

解除上文中的死锁,就是当thread1获取完lockA后释放,再获取lockB, 在上文中代码变更如下即可
synchronized(lockA){
System.out.println(threadName +"获得lockA的锁");
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized(lockB){
System.out.println(threadName +"获得lockB的锁");
}
此时就不会造成死锁了,输出内容如下:

监视锁monitor :是每个对象都有的一个隐藏字段。申请锁成功之后,monitor就会成为当前线程的唯一持有者。线程第一次执行monitorenter指令后,monitor的值由0变为1。当该线程再次遇到monitorenter指令后,就会将monitor继续累加1。这也是synchronized实现重入锁的原理。
总结:
同步操作的实现,需要给对象关联一个互斥体,这个互斥体就可以叫做锁。
锁的作用是,保证同一竞争资源在同一时刻只会有一个线程占有。
Java中锁的实现方式有两种:synchronized关键字和并发包中的锁类。
锁的优化策略有:锁消除、锁偏向、自适应自旋锁、锁粗化。
尽量不要在循环内使用锁,以减少资源消耗。
悲观锁 和 乐观锁 区别
1.悲观锁是当线程拿到资源时,就对资源上锁,并在提交后,才释放锁资源,其他线程才能使用资源。
2.乐观锁是当线程拿到资源时,上乐观锁,在提交之前,其他的锁也可以操作这个资源,当有冲突的时候,并发机制会保留前一个提交,打回后一个提交,让后一个线程重新获取资源后,再操作,然后提交。和git上传代码一样,两个线程都不是直接获取资源本身,而是先获取资源的两个copy版本,然后在这两个copy版本上修改。
3.悲观锁和乐观锁在并发量低的时候,性能差不多,但是在并发量高的时候, 乐观锁的性能远远优于悲观锁。
4.我们常用的synchronized是悲观锁,lock是乐观锁 。他们之间的区别详见于Java同步锁——lock与synchronized 的区别【转】;
5.还有一个乐观锁的例子 https://www.cnblogs.com/darrenqiao/p/9211447.html
参考:
Java中锁种类、实现方式_炎升的博客-CSDN博客_java锁的实现方式
本文汇总了关于锁的相关概念,包括死锁的四个条件及其避免策略,悲观锁和乐观锁的区别。通过实例分析了死锁的产生及解决办法,强调了锁在并发编程中的重要性,并探讨了Java中synchronized和ReentrantLock等锁的实现和优化策略。
728

被折叠的 条评论
为什么被折叠?



