JUC系列(二)八个有关锁的问题,让你吃透各种场景

在使用锁的时候一些有趣的问题

📣 📣 📣 📢📢📢
☀️☀️你好啊!小伙伴,我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。
📒 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️,擅长使用Java技术开发web项目和工具
📒 文章内容丰富:覆盖大部分java必学技术栈,前端,计算机基础,容器等方面的文章
📒 如果你也对Java感兴趣,关注小冷吧,一起探索Java技术的生态与进步,一起讨论Java技术的使用与学习
✏️高质量技术专栏专栏链接: 微服务数据结构netty单点登录SSMSpringCloudAlibaba
😝公众号😝想全栈的小冷,分享一些技术上的文章,以及解决问题的经验
当前专栏JUC系列

8锁现象

锁 ----> 刚new 出来的对象、class

8锁就是关于的锁的八个问题,下面也有四个demo类来阐述各种场景下锁的不同状态

demo1

两个问题:

正常模式下 在两个同步线程方法调用的时候 中途延时1s 会不会改变输出结果

答:不会影响输出顺序

正常模式下 同步方法内延时4s 会不会印象输出

答:不会影响输出顺序

  • 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话

demo代码

package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 * 1. 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
 * 2. 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo {
    public static void main(String[] args) {
        phone phone = new phone();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        }, "B").start();
    }
}

class phone {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
}

demo2

同步方法执行和普通方法执行顺序

答 :普通方法没有锁,所以普通方法先

两个对象执行会不会影响顺序

答:会 两个不同的对象锁也是不同的 ,对象1 还在等待,对象2调用的call方法不用等待所以先输出

  • 现在新增一个普通方法 问 先发短信还是先发hello 先输出hello
  • 新增两个对象 是先打电话还是先发短信
package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 * 3、 现在新增一个普通方法  问 先发短信还是先发hello 先输出hello
 * 4、 新增两个对象 是先打电话还是先发短信
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo2 {
    public static void main(String[] args) {
        //两个对象,现在是两个调用者所以是两个锁
        phone2 phone = new phone2();
        phone2 phone1 = new phone2();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone1.call();
        }, "B").start();
    }
}

class phone2 {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }


    //因为这里没有锁,不受锁的影响
    public void hello() {
        System.out.println("hello");
    }
}

demo3

一个对象调用静态同步方法会不会改动执行顺序

答: 不会 ,static是锁的类模版全局唯一,不会改变锁的执行交换顺序

两个对象调用静态同步方法会不会改变执行顺序

答:不会 static修饰的是类模版,锁的也是类模板而不是类对象,只要是这个类生成的对象,不管多少个都不会改变顺序

  • 一个对象 添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
  • 添加两个对象,分别调用静态同步方法 先打印 发短信 还是打电话?
package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 *  5、添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
 *  6、 添加两个对象,增加两个同步方法 先打印  发短信 还是打电话?
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo3 {
    public static void main(String[] args) {

        //不管多少对象, 使用的都是底层的唯一class所以不管怎么怎么改变结果都不会变,
        // 因为synchroized锁的是class对象,static修饰的方法 类一开始就加载了,
        new Thread(() -> {
            Phone3.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            Phone3.call();
        }, "B").start();
    }
}

//phone3是唯一的一个class
class Phone3 {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    // 这里 static 是静态方法 ,类一加载就有了,这个用的锁不再是 phone锁 而是class锁
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }

}

demo4

  • 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
  • 两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,

答: 不是同一个锁,谁执行快就输出谁,

package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 *  7、一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
 *  8、两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo4 {
    public static void main(String[] args) {
        Phone4 phone = new Phone4();
        //不管多少对象, 使用的都是底层的唯一class所以不管怎么怎么改变结果都不会变,
        // 因为synchroized锁的是class对象,static修饰的方法 类一开始就加载了,
        new Thread(() -> {
            Phone4.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        }, "B").start();
    }
}

//phone3是唯一的一个class
class Phone4 {
    //静态同步方法是锁的 class
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    //这里普通同步方法锁的是 phone
    public synchronized void call() {
        System.out.println("打电话");
    }

}

小结

我们通过八种不同的情况来理解锁在不同情况下的执行情况:

  • 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 现在新增一个普通方法 问 先发短信还是先发hello 先输出hello
  • 新增两个对象 是先打电话还是先发短信
  • 一个对象 添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
  • 添加两个对象,分别调用静态同步方法 先打印 发短信 还是打电话?
  • 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
  • 两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,

大家可以自己写一下代码看一些这些问题的结果,实践出效果,实践出理解

锁的东西,无非就是对象和 类模版

  1. new 出来的对象 就是锁具体的对象,比如普通同步方法
  2. 带有static 修饰的静态同步方法 锁的是类模版是全局唯一的对象class如 : class
<think>好的,我现在要帮用户解答Java后端面试的复习攻略。首先,我得想想用户的需求是什么。用户可能是准备找工作的应届生或者有一定经验的开发者,想要系统性地复习Java后端相关的知识,应对面试。用户可能希望得到一个全面的复习路线,涵盖重点知识点和常见面试题。 首先,我需要确定Java后端面试通常考察哪些内容。一般来说,包括Java基础、数据结构与算法、数据库、框架(如Spring)、系统设计、分布式系统、计算机网络、操作系统、项目经验、行为面试题等。此外,代码能力和实战经验也很重要。 接下来要考虑用户的背景。如果是应届生,可能更侧重基础知识和算法;如果有经验,可能更关注系统设计、分布式和高并发。但用户没有说明,所以应该覆盖全面,同时突出重点。 然后,需要组织内容的结构。分模块来讲解,每个模块列出重点知识点和常见问题,这样用户复习起来有条理。比如分Java基础、JVM、并发编程、数据库、框架、系统设计等模块。 每个模块里需要具体的关键点。例如Java基础中的集合类、多线程、IO/NIO等;JVM的内存模型、垃圾回收;并发编程的机制、线程池;数据库的索引、事务隔离级别;Spring的IoC、AOP、事务管理等。 还要注意结合实际面试题,比如HashMap的实现原理,ConcurrentHashMap的线程安全机制,MySQL的索引优化,Redis的持久化方式,分布式事务解决方案等。这些是高频问题,需要详细解释。 系统设计部分可能包括如何设计一个秒杀系统,分布式的实现,CAP理论等。这部分需要用户有一定的实战经验,或者了解常见的设计模式。 另外,代码能力部分要提醒用户多刷LeetCode,特别是数组、链表、叉树、动态规划等题目。面试中可能会有手写代码的环节,所以熟练度很重要。 项目经验方面,用户需要准备自己参与过的项目,能够清晰描述项目架构、遇到的挑战和解决方案。最好有技术难点和优化经验,这能展示实际能力。 行为面试题如职业规划、优缺点、团队协作等问题也不能忽视,虽然技术为主,但综合能力同样重要。 最后,需要提醒用户复习时注意理解原理,而不仅仅是记忆,面试官喜欢深入追问。同时建议模拟面试,查漏补缺,保持良好心态。 可能用户没有提到的深层需求是希望高效复习,抓住重点,避免浪费时间在不重要的内容上。因此,攻略需要简洁明了,突出重点,提供明确的复习方向和学习资源推荐。 总结下来,回答的结构应该是分模块列出重点,每个模块给出关键知识点和常见问题,最后补充复习建议。这样用户可以根据自己的情况有针对性地准备,提高面试成功率。</think>以下是Java后端面试的系统复习攻略(按优先级排序),附高频考点和复习建议: --- ### 一、Java核心基础(必须吃透) 1. **集合框架** - HashMap底层原理(数组+链表/红黑树)、ConcurrentHashMap分段/CAS机制 - ArrayList vs LinkedList时间复杂度对比 - 快速失败(fail-fast)和安全失败(fail-safe)区别 *高频题:HashMap扩容机制?为什么线程不安全?* 2. **多线程与并发** - synchronized底层原理(对象头、升级过程) - volatile关键字与内存可见性 - ThreadLocal原理及内存泄漏问题 - 线程池参数(corePoolSize、workQueue等)与执行流程 *高频题:如何用ReentrantLock实现公平?* 3. **JVM** - 内存模型(堆、栈、方法区) - 垃圾回收算法(CMS/G1/ZGC对比) - 类加载机制与双亲委派 *高频题:Full GC触发条件?如何排查OOM问题?* --- ### 、数据库(MySQL+Redis重点) 1. **MySQL** - 索引原理(B+树 vs Hash) - 事务隔离级别与MVCC实现 - 慢查询优化(EXPLAIN执行计划) *高频题:为什么用自增主键?如何解决幻读?* 2. **Redis** - 持久化机制(RDB/AOF混合持久化) - 缓存穿透/雪崩/击穿解决方案 - 分布式实现(Redlock算法) *高频题:Redis为什么快?集群数据如何分片?* --- ### 三、框架体系(Spring全家桶) 1. **Spring Framework** - IOC容器启动流程(Bean生命周期) - AOP实现原理(动态代理/CGLIB) - 声明式事务传播机制 *高频题:循环依赖如何解决?* 2. **Spring Boot** - 自动配置原理(@EnableAutoConfiguration) - Starter组件设计思想 - 监控端点(Actuator)使用 3. **Spring Cloud** - 服务注册发现(Eureka/Nacos) - 熔断降级(Hystrix/Sentinel) - 网关路由(Zuul/Gateway) --- ### 四、系统设计(社招重点) 1. **分布式系统** - CAP定理与BASE理论 - 分布式事务解决方案(Seata/TCC) - 分库分表策略(ShardingSphere) 2. **高并发场景** - 秒杀系统设计(库存扣减方案) - 限流算法(令牌桶/漏桶) - 消息队列应用(Kafka顺序消费) --- ### 五、编码能力(必须手写) 1. **LeetCode高频题** - 链表:反转链表、环形链表检测 - 叉树:层序遍历、最近公共祖先 - DP:最长递增子序列、背包问题 *建议:每天保持3道中等难度练习* --- ### 六、项目复盘(必考) 1. **STAR法则描述项目** - 突出技术难点(如:如何解决分布式失效问题) - 性能优化指标(QPS从1000提升到5000) - 体现架构设计能力(微服务拆分策略) --- ### 复习建议 1. **优先级策略**:基础 > 数据库 > 框架 > 系统设计 2. **实战演练**:本地搭建Redis集群/实现简易RPC框架 3. **模拟面试**:使用《Java面试突击手册》自测 4. **避坑指南**:避免死记硬背,重点理解设计思想 *附资源推荐:* - 书籍:《Java并发编程实战》《凤凰架构》 - 视频:尚硅谷JUC/黑马Spring源码 - 网站:JavaGuide、小林coding图解系统
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冷环渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值