note-java
Java学习记录
IDEA
改善体验的设置
- Change font size with Command + Mouse Wheel:用鼠标滚轮改变字体大小
- Sync setting across:选择设置同步的范围
- IntelliJ IDEA instances only
- All JetBrains IDE Products
- File adn Code Templates:修改新建文件的默认模板(类,接口)
1 | /** |
- Live Templates:自定义快捷短语(比如sout)
- File > New Projects Setup:设置新项目的Settings和Structure等
插件
-
CodeGlance Pro:显示当前文件代码概览
-
Github Copilot:AI
-
Key Promoter X:学习快捷键
-
Translation:翻译
-
Alibaba Java Code Guidelines:阿里巴巴代码规范
-
Rainbow Brackets:彩虹括号,清晰代码
-
Maven Helper:Maven依赖管理
-
Kafkalytic:Kafka管理
-
Zoolytic:ZooKeeper管理
快捷键
-
ctrl + H:查看当前类或接口的层次结构
-
ctrl + shift + B:查看方法或对象的实现
-
ctrl + E:查看最近的文件
-
ctrl + shift + E:查看最近的位置(窗口)
-
alt + home:使用导航
-
shift + esc:隐藏当前活跃窗口
-
代码编辑
- ctrl + shift + alt + T:打开重构框
-
alt + 7:查看Structure窗口
-
ctrl + alt + [/]:查看上一个/下一个 IDEA窗口
Maven
Java并发
Java多线程入门 类和接口
JDK提供了Thread类和Runnalble接口来让我们实现自己的“线程”类
Thread类
继承Thread类,并重写run方法
构造函数:
1 | private void init(ThreadGroup g, Runnable target, String name, |
Thread类常用方法
- currentThread():静态方法,返回对当前正在执行的线程对象的引用;
- start():开始执行线程的方法,java虚拟机会调用线程内的run()方法;
- yield():yield在英语里有放弃的意思,同样,这里的yield()指的是当前线程愿意让出对当前处理器的占用。这里需要注意的是,就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程的;
- sleep():静态方法,使当前线程睡眠一段时间;
- join():使当前线程等待另一个线程执行完毕之后再继续执行,内部调用的是Object类的wait方法实现的;
Runnable接口
实现Runnable接口的run方法
Runnable是一个函数式接口,这意味着我们可以使用Java 8的函数式编程来简化代码
1 | new Thread(() -> { |
Thread类 对比 Runnable接口
- 由于Java“单继承,多实现”的特性,Runnable接口使用起来比Thread更灵活。
- Runnable接口出现更符合面向对象,将线程单独进行对象的封装
- Runnable接口出现,降低了线程对象和线程任务的耦合性
- 如果使用线程时不需要使用Thread类的诸多方法,显然使用Runnable接口更为轻量
乐观锁和悲观锁
悲观锁:总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放
乐观锁:总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了
高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增加系统的性能开销。并且,悲观锁还可能会存在死锁问题,影响代码的正常运行。乐观锁相比悲观锁来说,不存在锁竞争造成线程阻塞,也不会有死锁的问题,在性能上往往会更胜一筹。不过,如果冲突频繁发生(写占比非常多的情况),会频繁失败和重试,这样同样会非常影响性能,导致 CPU 飙升
乐观锁一般会使用版本号机制或 CAS 算法实现,CAS 算法相对来说更多一些,这里需要格外注意。CAS 的全称是 Compare And Swap(比较与交换) ,用于实现乐观锁,被广泛应用于各大框架中
CAS 的思想很简单,就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新
- CAS 算法的问题
- ABA 问题:解决思路是在变量前面追加上版本号或者时间戳。JDK 1.5 以后的 AtomicStampedReference 类就是用来解决 ABA 问题的,其中的 compareAndSet() 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值
- 循环时间长开销大:CAS 经常会用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功。如果 JVM 能支持处理器提供的 pause 指令那么效率会有一定的提升
- 只能保证一个共享变量的原子操作:从 JDK 1.5 开始,提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作
Java内存模型
Redspider社区的《深入浅出Java多线程》
简介:站在巨人的肩上,我们可以看得更远。本书内容的主要来源有博客、书籍、论文,对于一些已经叙述得很清晰的知识点我们直接引用在本书中;对于一些没有讲解清楚的知识点,我们加以画图或者编写Demo进行加工;而对于一些模棱两可的知识点,本书在查阅了大量资料的情况下,给出最合理的解释
Spring
mini-spring
可拓展部分:只简单实现BeforeAdvice,有兴趣的同学可以帮忙实现另外几种Advice。定义MethodBeforeAdviceInterceptor拦截器,在执行被代理方法之前,先执行BeforeAdvice的方法。BeforeAdvice、AfterAdvice、AfterReturningAdvice、ThrowsAdvice
1 | └─springframework |
RPC
JavaGuide的《手写 RPC 框架》
一款基于 Netty+Kyro+Zookeeper 实现的 RPC 框架
1 | ├─example-client:示例客户端 |
dubbo spi:SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。
MISC
类和对象
equals与hashcode
- equals方法
- 比较两个对象值是否相等
- 默认(Objects)与==比较相同,即比较对象地址是否相同
- 重写equals方法:
- 例如,String类就重写equals方法,比较的是字符串内容
- hashcode方法
- 每个对象都有一个hashcode,默认是对象地址,是一个int值,用于在哈希结构中散列到某个位置
- 对于hashmap来说,如果两个对象相等,那么它们的hashCode值一定要相同,否则在哈希表的不同位置会出现相等的对象
- 例如,在hashmap中,索引对象时先通过hashcode找到位置,再通过equals方法比较对象的值是否相同,如果相同,则不再放入hashmap;如果不同,则添加到链尾
- 重写equals必须重写hashcode
- 确保equals时的hashcode能覆盖!
- 重写equals前:因为hashcode的值默认是对象地址,不同的对象它们的地址不同,hashcode就不同,equals默认比较的也是地址,所以equals的结果是false
- 重写equals后:不同对象它们equals结果则可能为true,而在hashmap中,存入这两个对象(通过hashcode),会存到不同的位置,这就导致了hashmap中出现了两个值相同的对象,违背了hashmap的原则。所以,需要重写hashcode方法,使值相同的对象的hashcode值也必须相同。