1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-08-01 16:28:03 +08:00

Compare commits

..

6 Commits

Author SHA1 Message Date
Guide
0574285032
Merge pull request #2436 from qiliq/patch-2
Update sql-questions-03.md
2024-07-24 16:51:56 +08:00
玘珞
e1e809c9d7
Update sql-questions-03.md
表里只有10条数据,修改11次作答记录为10次
2024-07-24 11:42:55 +08:00
Guide
b5491ffb35
Merge pull request #2434 from DavidWhom/patch-2
Update other-network-questions.md
2024-07-22 20:18:05 +08:00
Guide
6232dd3f14 [docs add]新增文章程序员最该拿的几种高含金量证书 2024-07-22 19:26:12 +08:00
Guide
4f87ee846e [docs update]完善问题:面向对象和面向过程的区别 2024-07-21 12:56:33 +08:00
Guevara
47df1a878b
Update other-network-questions.md
移除多余的括号
2024-07-19 14:26:16 +08:00
9 changed files with 311 additions and 197 deletions

View File

@ -34,6 +34,7 @@ export const highQualityTechnicalArticles = arraySidebar([
prefix: "programmer/",
collapsible: false,
children: [
"high-value-certifications-for-programmers",
"how-do-programmers-publish-a-technical-book",
"efficient-book-publishing-and-practice-guide",
],

View File

@ -48,14 +48,14 @@ export default hopeTheme({
notice: [
{
path: "/",
title: "知识星球",
title: "PDF面试资料2024版",
showOnce: true,
content:
"专属面试小册/一对一交流/简历修改/专属求职指南,欢迎加入 JavaGuide 知识星球。",
"2024最新版原创PDF面试资料来啦涵盖 Java 核心、数据库、缓存、分布式、设计模式、智力题等内容,非常全面!",
actions: [
{
text: "前往了解",
link: "https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html",
text: "点击领取",
link: "https://oss.javaguide.cn/backend-notekbook/official-account-traffic-backend-notebook-with-data-screenshot.png",
type: "primary",
},
],

View File

@ -94,7 +94,7 @@ tag:
- **ARPAddress Resolution Protocol地址解析协议**ARP 协议解决的是网络层地址和链路层地址之间的转换问题。因为一个 IP 数据报在物理上传输的过程中,总是需要知道下一跳(物理上的下一个目的地)该去往何处,但 IP 地址属于逻辑地址,而 MAC 地址才是物理地址ARP 协议解决了 IP 地址转 MAC 地址的一些问题。
- **ICMPInternet Control Message Protocol互联网控制报文协议**一种用于传输网络状态和错误消息的协议常用于网络诊断和故障排除。例如Ping 工具就使用了 ICMP 协议来测试网络连通性。
- **NATNetwork Address Translation网络地址转换协议**NAT 协议的应用场景如同它的名称——网络地址转换应用于内部网到外部网的地址转换过程中。具体地说在一个小的子网局域网LAN各主机使用的是同一个 LAN 下的 IP 地址,但在该 LAN 以外在广域网WAN需要一个统一的 IP 地址来标识该 LAN 在整个 Internet 上的位置。
- **OSPFOpen Shortest Path First开放式最短路径优先** 一种内部网关协议Interior Gateway ProtocolIGP也是广泛使用的一种动态路由协议基于链路状态算法考虑了链路的带宽、延迟等因素来选择最佳路径。
- **OSPFOpen Shortest Path First开放式最短路径优先**一种内部网关协议Interior Gateway ProtocolIGP也是广泛使用的一种动态路由协议基于链路状态算法考虑了链路的带宽、延迟等因素来选择最佳路径。
- **RIP(Routing Information Protocol路由信息协议**一种内部网关协议Interior Gateway ProtocolIGP也是一种动态路由协议基于距离向量算法使用固定的跳数作为度量标准选择跳数最少的路径作为最佳路径。
- **BGPBorder Gateway Protocol边界网关协议**一种用来在路由选择域之间交换网络层可达性信息Network Layer Reachability InformationNLRI的路由选择协议具有高度的灵活性和可扩展性。

View File

@ -216,9 +216,9 @@ WHERE info.exam_id = record.exam_id
| total_pv | complete_pv | complete_exam_cnt |
| -------- | ----------- | ----------------- |
| 11 | 7 | 2 |
| 10 | 7 | 2 |
解释:表示截止当前,有 11 次试卷作答记录,已完成的作答次数为 7 次(中途退出的为未完成状态,其交卷时间和份数为 NULL已完成的试卷有 9001 和 9002 两份。
解释:表示截止当前,有 10 次试卷作答记录,已完成的作答次数为 7 次(中途退出的为未完成状态,其交卷时间和份数为 NULL已完成的试卷有 9001 和 9002 两份。
**思路** 这题一看到统计次数,肯定第一时间就要想到用`COUNT`这个函数来解决,问题是要统计不同的记录,该怎么来写?使用子查询就能解决这个题目(这题用 case when 也能写出来,解法类似,逻辑不同而已);首先在做这个题之前,让我们先来了解一下`COUNT`的基本用法;

View File

@ -0,0 +1,114 @@
---
title: 程序员最该拿的几种高含金量证书
category: 技术文章精选集
tag:
- 程序员
---
证书是能有效证明自己能力的好东西它就是你实力的象征。在短短的面试时间内证书可以为你加不少分。通过考证来提升自己是一种性价比很高的办法。不过相比金融、建筑、医疗等行业IT 行业的职业资格证书并没有那么多。
下面我总结了一下程序员可以考的一些常见证书。
## 软考
全国计算机技术与软件专业技术资格(水平)考试,简称“软考”,是国内认可度较高的一项计算机技术资格认证。尽管一些人吐槽其实际价值,但在特定领域和情况下,它还是非常有用的,例如软考证书在国企和事业单位中具有较高的认可度、在某些城市软考证书可以用于积分落户、可用于个税补贴。
软考有初、中、高三个级别建议直接考高级。相比于PMP项目管理专业人士认证软考高项的难度更大特别是论文部分绝大部分人都挂在了论文部分。过了软考高项在一些单位可以内部挂证每个月多拿几百。
![软考高级证书](https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/ruankao-advanced-certification%20.jpg)
官网地址:<https://www.ruankao.org.cn/>
备考建议:[2024年上半年一次通过软考高级架构师考试的备考秘诀 - 阿里云开发者](https://mp.weixin.qq.com/s/9aUXHJ7dXgrHuT19jRhCnw)
## PAT
攀拓计算机能力测评PAT是一个专注于考察算法能力的测评体系由浙江大学主办。该测评分为四个级别基础级、乙级、甲级和顶级。
通过PAT测评并达到联盟企业规定的相应评级和分数可以跳过学历门槛免除筛选简历和笔试环节直接获得面试机会。具体有哪些公司可以去官网看看<https://www.patest.cn/company>
对于考研浙江大学的同学来说PAT甲级成绩在一年内可以作为硕士研究生招生考试上机复试成绩。
![PAT甲级成绩作用](https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/pat-enterprise-alliance.png)
## PMP
PMPProject Management Professional认证由美国项目管理协会PMI提供是全球范围内认可度最高的项目管理专业人士资格认证。PMP认证旨在提升项目管理专业人士的知识和技能确保项目顺利完成。
![PMP 证书](https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/pmp-certification.png)
PMP 是“一证在手全球通用”的资格认证对项目管理人士来说PMP 证书含金量还是比较高的。放眼全球,很多成功企业都会将 PMP 认证作为项目经理的入职标准。
但是!真正有价值的不是 PMP 证书而是《PMBOK》 那套项目管理体系在《PMBOK》PMP 考试指定用书)中也包含了非常多商业活动、实业项目、组织规划、建筑行业等各个领域的项目案例。
另外PMP 证书不是一个高大上的证书,而是一个基础的证书。
## ACP
ACPAgile Certified Practitioner认证同样由美国项目管理协会PMI提供是项目管理领域的另一个重要认证。与PMPProject Management Professional注重传统的瀑布方法论不同ACP专注于敏捷项目管理方法论如Scrum、Kanban、Lean、Extreme ProgrammingXP等。
## OCP
Oracle Certified ProfessionalOCP是Oracle公司提供的一项专业认证专注于Oracle数据库及相关技术。这个认证旨在验证和认证个人在使用和管理Oracle数据库方面的专业知识和技能。
下图展示了 Oracle 认证的不同路径和相应的认证级别分别是核心路径Core Track和专业路径Speciality Track
![OCP 认证路径](https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/oracle-certified-professional.jpg)
## 阿里云认证
阿里云Alibaba Cloud提供的专业认证认证方向包括云计算、大数据、人工智能、Devops等。职业认证分为 ACA、ACP、ACE 三个等级除了职业认证之外还有一个开发者Clouder认证这是专门为开发者设立的专项技能认证。
![](https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/aliyun-professional-certification.png)
官网地址:<https://edu.aliyun.com/certification/>
## 华为认证
华为认证是由华为技术有限公司提供的面向ICT信息与通信技术领域的专业认证认证方向包括网络、存储、云计算、大数据、人工智能等非常庞大的认证体系。
![](https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/huawei-professional-certification.png)
## AWS 认证
AWS云认证考试是AWS云计算服务的官方认证考试旨在验证IT专业人士在设计、部署和管理AWS基础架构方面的技能。
AWS 认证分为多个级别包括基础级、从业者级、助理级、专业级和专家级Specialty涵盖多个角色和技能
- **基础级别**AWS Certified Cloud Practitioner适合初学者验证对 AWS 基础知识的理解,是最简单的入门认证。
- **助理级别**:包括 AWS Certified Solutions Architect Associate、AWS Certified Developer Associate 和 AWS Certified SysOps Administrator Associate适合中级专业人士验证其设计、开发和管理 AWS 应用的能力。
- **专业级别**:包括 AWS Certified Solutions Architect Professional 和 AWS Certified DevOps Engineer Professional适合高级专业人士验证其在复杂和大规模 AWS 环境中的能力。
- **专家级别**:包括 AWS Certified Advanced Networking Specialty、AWS Certified Big Data Specialty 等,专注于特定技术领域的深度知识和技能。
备考建议:[小白入门云计算的最佳方式是去考一张AWS的证书附备考经验](https://mp.weixin.qq.com/s/xAqNOnfZ05GDRuUbAiMHIA)
## Google Cloud 认证
与 AWS 认证不同Google Cloud 认证只有一门助理级认证Associate Cloud Engineer其他大部分为专业级专家级认证。
备考建议:[如何备考谷歌云认证](https://mp.weixin.qq.com/s/Vw5LGPI_akA7TQl1FMygWw)
官网地址:<https://cloud.google.com/certification>
## 微软认证
微软的认证体系主要针对其 Azure 云平台,分为基础级别、助理级别和专家级别,认证方向包括云计算、数据管理、开发、生产力工具等。
![](https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/microsoft-certification.png)
## Elastic 认证
Elastic 认证是由 Elastic 公司提供的一系列专业认证,旨在验证个人在使用 Elastic Stack包括 Elasticsearch、Logstash、Kibana 、Beats 等)方面的技能和知识。
如果你在日常开发核心工作是负责 ElasticSearch 相关业务的话,还是比较建议考的,含金量挺高。
目前 Elastic 认证证书分为四类Elastic Certified Engineer、Elastic Certified Analyst、Elastic Certified Observability Engineer、Elastic Certified SIEM Specialist。
比较建议考 **Elastic Certified Engineer**,这个是 Elastic Stack 的基础认证,考察安装、配置、管理和维护 Elasticsearch 集群等核心技能。
![](https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/elastic-certified-engineer-certification.png)
## 其他
- PostgreSQL 认证:国内的 PostgreSQL 认证分为专员级PCA、专家级PCP和大师级PCM主要考查 PostgreSQL 数据库管理和优化,价格略贵,不是很推荐。
- Kubernetes 认证Cloud Native Computing Foundation (CNCF) 提供了几个官方认证例如Certified Kubernetes Administrator (CKA)、Certified Kubernetes Application Developer (CKAD)主要考察Kubernetes 方面的技能和知识。

View File

@ -23,6 +23,7 @@
## 程序员
- [程序员最该拿的几种高含金量证书](./programmer/high-value-certifications-for-programmers.md)
- [程序员怎样出版一本技术书](./programmer/how-do-programmers-publish-a-technical-book.md)
- [程序员高效出书避坑和实践指南](./programmer/efficient-book-publishing-and-practice-guide.md)

View File

@ -18,14 +18,26 @@ head:
### 面向对象和面向过程的区别
两者的主要区别在于解决问题的方式不同:
面向过程编程Procedural-Oriented ProgrammingPOP和面向对象编程Object-Oriented ProgrammingOOP是两种常见的编程范式两者的主要区别在于解决问题的方式不同:
- 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
- 面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。
- **面向过程编程POP**面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
- **面向对象编程OOP**面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。
另外,面向对象开发的程序一般更易维护、易复用、易扩展。
相比较于 POPOOP 开发的程序一般具有下面这些优点:
相关 issue : [面向过程:面向过程性能比面向对象高??](https://github.com/Snailclimb/JavaGuide/issues/431) 。
- **易维护**由于良好的结构和封装性OOP 程序通常更容易维护。
- **易复用**通过继承和多态OOP 设计使得代码更具复用性,方便扩展功能。
- **易扩展**:模块化设计使得系统扩展变得更加容易和灵活。
POP 的编程方式通常更为简单和直接,适合处理一些较简单的任务。
POP 和 OOP 的性能差异主要取决于它们的运行机制,而不仅仅是编程范式本身。因此,简单地比较两者的性能是一个常见的误区(相关 issue : [面向过程:面向过程性能比面向对象高??](https://github.com/Snailclimb/JavaGuide/issues/431) )。
![ POP 和 OOP 性能比较不合适](https://oss.javaguide.cn/github/javaguide/java/basis/pop-vs-oop-performance.png)
在选择编程范式时,性能并不是唯一的考虑因素。代码的可维护性、可扩展性和开发效率同样重要。
现代编程语言基本都支持多种编程范式,既可以用来进行面向过程编程,也可以进行面向对象编程。
下面是一个求圆的面积和周长的示例,简单分别展示了面向对象和面向过程两种不同的解决方案。

View File

@ -15,7 +15,7 @@ tag:
`Atomic` 类依赖于 CASCompare-And-Swap比较并交换乐观锁来保证其方法的原子性而不需要使用传统的锁机制`synchronized` 块或 `ReentrantLock`)。
这篇文章我们直接介绍 Atomic 原子类的概念,具体实现原理可以阅读笔者写的这篇文章:[CAS 详解](./cas.md)。
这篇文章我们介绍 Atomic 原子类的概念,具体实现原理可以阅读笔者写的这篇文章:[CAS 详解](./cas.md)。
![JUC原子类概览](https://oss.javaguide.cn/github/javaguide/java/JUC%E5%8E%9F%E5%AD%90%E7%B1%BB%E6%A6%82%E8%A7%88.png)
@ -140,26 +140,55 @@ public final void lazySet(int i, int newValue)//最终 将index=i 位置的元
**`AtomicIntegerArray` 类使用示例** :
```java
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayTest {
public static void main(String[] args) {
int temvalue = 0;
int[] nums = { 1, 2, 3, 4, 5, 6 };
AtomicIntegerArray i = new AtomicIntegerArray(nums);
for (int j = 0; j < nums.length; j++) {
System.out.println(i.get(j));
}
temvalue = i.getAndSet(0, 2);
System.out.println("temvalue:" + temvalue + "; i:" + i);
temvalue = i.getAndIncrement(0);
System.out.println("temvalue:" + temvalue + "; i:" + i);
temvalue = i.getAndAdd(0, 5);
System.out.println("temvalue:" + temvalue + "; i:" + i);
}
int[] nums = {1, 2, 3, 4, 5, 6};
// 创建 AtomicIntegerArray
AtomicIntegerArray atomicArray = new AtomicIntegerArray(nums);
// 打印 AtomicIntegerArray 中的初始值
System.out.println("Initial values in AtomicIntegerArray:");
for (int j = 0; j < nums.length; j++) {
System.out.print("Index " + j + ": " + atomicArray.get(j) + " ");
}
// 使用 getAndSet 方法将索引 0 处的值设置为 2并返回旧值
int tempValue = atomicArray.getAndSet(0, 2);
System.out.println("\nAfter getAndSet(0, 2):");
System.out.println("Returned value: " + tempValue);
for (int j = 0; j < atomicArray.length(); j++) {
System.out.print("Index " + j + ": " + atomicArray.get(j) + " ");
}
// 使用 getAndIncrement 方法将索引 0 处的值加 1并返回旧值
tempValue = atomicArray.getAndIncrement(0);
System.out.println("\nAfter getAndIncrement(0):");
System.out.println("Returned value: " + tempValue);
for (int j = 0; j < atomicArray.length(); j++) {
System.out.print("Index " + j + ": " + atomicArray.get(j) + " ");
}
// 使用 getAndAdd 方法将索引 0 处的值增加 5并返回旧值
tempValue = atomicArray.getAndAdd(0, 5);
System.out.println("\nAfter getAndAdd(0, 5):");
System.out.println("Returned value: " + tempValue);
for (int j = 0; j < atomicArray.length(); j++) {
System.out.print("Index " + j + ": " + atomicArray.get(j) + " ");
}
```
输出:
```plain
Initial values in AtomicIntegerArray:
Index 0: 1 Index 1: 2 Index 2: 3 Index 3: 4 Index 4: 5 Index 5: 6
After getAndSet(0, 2):
Returned value: 1
Index 0: 2 Index 1: 2 Index 2: 3 Index 3: 4 Index 4: 5 Index 5: 6
After getAndIncrement(0):
Returned value: 2
Index 0: 3 Index 1: 2 Index 2: 3 Index 3: 4 Index 4: 5 Index 5: 6
After getAndAdd(0, 5):
Returned value: 3
Index 0: 8 Index 1: 2 Index 2: 3 Index 3: 4 Index 4: 5 Index 5: 6
```
## 引用类型原子类
@ -175,174 +204,133 @@ public class AtomicIntegerArrayTest {
**`AtomicReference` 类使用示例** :
```java
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceTest {
public static void main(String[] args) {
AtomicReference < Person > ar = new AtomicReference < Person > ();
Person person = new Person("SnailClimb", 22);
ar.set(person);
Person updatePerson = new Person("Daisy", 20);
ar.compareAndSet(person, updatePerson);
System.out.println(ar.get().getName());
System.out.println(ar.get().getAge());
}
}
// Person 类
class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//省略getter/setter和toString
}
// 创建 AtomicReference 对象并设置初始值
AtomicReference<Person> ar = new AtomicReference<>(new Person("SnailClimb", 22));
// 打印初始值
System.out.println("Initial Person: " + ar.get().toString());
// 更新值
Person updatePerson = new Person("Daisy", 20);
ar.compareAndSet(ar.get(), updatePerson);
// 打印更新后的值
System.out.println("Updated Person: " + ar.get().toString());
// 尝试再次更新
Person anotherUpdatePerson = new Person("John", 30);
boolean isUpdated = ar.compareAndSet(updatePerson, anotherUpdatePerson);
// 打印是否更新成功及最终值
System.out.println("Second Update Success: " + isUpdated);
System.out.println("Final Person: " + ar.get().toString());
```
上述代码首先创建了一个 `Person` 对象,然后把 `Person` 对象设置进 `AtomicReference` 对象中,然后调用 `compareAndSet` 方法,该方法就是通过 CAS 操作设置 ar。如果 ar 的值为 `person` 的话,则将其设置为 `updatePerson`。实现原理与 `AtomicInteger` 类中的 `compareAndSet` 方法相同。运行上面的代码后的输出结果如下:
输出:
```plain
Daisy
20
Initial Person: Person{name='SnailClimb', age=22}
Updated Person: Person{name='Daisy', age=20}
Second Update Success: true
Final Person: Person{name='John', age=30}
```
**`AtomicStampedReference` 类使用示例** :
```java
import java.util.concurrent.atomic.AtomicStampedReference;
// 创建一个 AtomicStampedReference 对象,初始值为 "SnailClimb",初始版本号为 1
AtomicStampedReference<String> asr = new AtomicStampedReference<>("SnailClimb", 1);
public class AtomicStampedReferenceDemo {
public static void main(String[] args) {
// 实例化、取当前值和 stamp 值
final Integer initialRef = 0, initialStamp = 0;
final AtomicStampedReference<Integer> asr = new AtomicStampedReference<>(initialRef, initialStamp);
System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());
// 打印初始值和版本号
int[] initialStamp = new int[1];
String initialRef = asr.get(initialStamp);
System.out.println("Initial Reference: " + initialRef + ", Initial Stamp: " + initialStamp[0]);
// compare and set
final Integer newReference = 666, newStamp = 999;
final boolean casResult = asr.compareAndSet(initialRef, newReference, initialStamp, newStamp);
System.out.println("currentValue=" + asr.getReference()
+ ", currentStamp=" + asr.getStamp()
+ ", casResult=" + casResult);
// 更新值和版本号
int oldStamp = initialStamp[0];
String oldRef = initialRef;
String newRef = "Daisy";
int newStamp = oldStamp + 1;
// 获取当前的值和当前的 stamp 值
int[] arr = new int[1];
final Integer currentValue = asr.get(arr);
final int currentStamp = arr[0];
System.out.println("currentValue=" + currentValue + ", currentStamp=" + currentStamp);
boolean isUpdated = asr.compareAndSet(oldRef, newRef, oldStamp, newStamp);
System.out.println("Update Success: " + isUpdated);
// 单独设置 stamp 值
final boolean attemptStampResult = asr.attemptStamp(newReference, 88);
System.out.println("currentValue=" + asr.getReference()
+ ", currentStamp=" + asr.getStamp()
+ ", attemptStampResult=" + attemptStampResult);
// 打印更新后的值和版本号
int[] updatedStamp = new int[1];
String updatedRef = asr.get(updatedStamp);
System.out.println("Updated Reference: " + updatedRef + ", Updated Stamp: " + updatedStamp[0]);
// 重新设置当前值和 stamp 值
asr.set(initialRef, initialStamp);
System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());
// 尝试用错误的版本号更新
boolean isUpdatedWithWrongStamp = asr.compareAndSet(newRef, "John", oldStamp, newStamp + 1);
System.out.println("Update with Wrong Stamp Success: " + isUpdatedWithWrongStamp);
// [不推荐使用,除非搞清楚注释的意思了] weak compare and set
// 困惑weakCompareAndSet 这个方法最终还是调用 compareAndSet 方法。[版本: jdk-8u191]
// 但是注释上写着 "May fail spuriously and does not provide ordering guarantees,
// so is only rarely an appropriate alternative to compareAndSet."
// todo 感觉有可能是 jvm 通过方法名在 native 方法里面做了转发
final boolean wCasResult = asr.weakCompareAndSet(initialRef, newReference, initialStamp, newStamp);
System.out.println("currentValue=" + asr.getReference()
+ ", currentStamp=" + asr.getStamp()
+ ", wCasResult=" + wCasResult);
}
}
// 打印最终的值和版本号
int[] finalStamp = new int[1];
String finalRef = asr.get(finalStamp);
System.out.println("Final Reference: " + finalRef + ", Final Stamp: " + finalStamp[0]);
```
输出结果如下:
```plain
currentValue=0, currentStamp=0
currentValue=666, currentStamp=999, casResult=true
currentValue=666, currentStamp=999
currentValue=666, currentStamp=88, attemptStampResult=true
currentValue=0, currentStamp=0
currentValue=666, currentStamp=999, wCasResult=true
Initial Reference: SnailClimb, Initial Stamp: 1
Update Success: true
Updated Reference: Daisy, Updated Stamp: 2
Update with Wrong Stamp Success: false
Final Reference: Daisy, Final Stamp: 2
```
**`AtomicMarkableReference` 类使用示例** :
```java
import java.util.concurrent.atomic.AtomicMarkableReference;
// 创建一个 AtomicMarkableReference 对象,初始值为 "SnailClimb",初始标记为 false
AtomicMarkableReference<String> amr = new AtomicMarkableReference<>("SnailClimb", false);
public class AtomicMarkableReferenceDemo {
public static void main(String[] args) {
// 实例化、取当前值和 mark 值
final Boolean initialRef = null, initialMark = false;
final AtomicMarkableReference<Boolean> amr = new AtomicMarkableReference<>(initialRef, initialMark);
System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());
// 打印初始值和标记
boolean[] initialMark = new boolean[1];
String initialRef = amr.get(initialMark);
System.out.println("Initial Reference: " + initialRef + ", Initial Mark: " + initialMark[0]);
// compare and set
final Boolean newReference1 = true, newMark1 = true;
final boolean casResult = amr.compareAndSet(initialRef, newReference1, initialMark, newMark1);
System.out.println("currentValue=" + amr.getReference()
+ ", currentMark=" + amr.isMarked()
+ ", casResult=" + casResult);
// 更新值和标记
String oldRef = initialRef;
String newRef = "Daisy";
boolean oldMark = initialMark[0];
boolean newMark = true;
// 获取当前的值和当前的 mark 值
boolean[] arr = new boolean[1];
final Boolean currentValue = amr.get(arr);
final boolean currentMark = arr[0];
System.out.println("currentValue=" + currentValue + ", currentMark=" + currentMark);
boolean isUpdated = amr.compareAndSet(oldRef, newRef, oldMark, newMark);
System.out.println("Update Success: " + isUpdated);
// 单独设置 mark 值
final boolean attemptMarkResult = amr.attemptMark(newReference1, false);
System.out.println("currentValue=" + amr.getReference()
+ ", currentMark=" + amr.isMarked()
+ ", attemptMarkResult=" + attemptMarkResult);
// 打印更新后的值和标记
boolean[] updatedMark = new boolean[1];
String updatedRef = amr.get(updatedMark);
System.out.println("Updated Reference: " + updatedRef + ", Updated Mark: " + updatedMark[0]);
// 重新设置当前值和 mark 值
amr.set(initialRef, initialMark);
System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());
// 尝试用错误的标记更新
boolean isUpdatedWithWrongMark = amr.compareAndSet(newRef, "John", oldMark, !newMark);
System.out.println("Update with Wrong Mark Success: " + isUpdatedWithWrongMark);
// [不推荐使用,除非搞清楚注释的意思了] weak compare and set
// 困惑weakCompareAndSet 这个方法最终还是调用 compareAndSet 方法。[版本: jdk-8u191]
// 但是注释上写着 "May fail spuriously and does not provide ordering guarantees,
// so is only rarely an appropriate alternative to compareAndSet."
// todo 感觉有可能是 jvm 通过方法名在 native 方法里面做了转发
final boolean wCasResult = amr.weakCompareAndSet(initialRef, newReference1, initialMark, newMark1);
System.out.println("currentValue=" + amr.getReference()
+ ", currentMark=" + amr.isMarked()
+ ", wCasResult=" + wCasResult);
}
}
// 打印最终的值和标记
boolean[] finalMark = new boolean[1];
String finalRef = amr.get(finalMark);
System.out.println("Final Reference: " + finalRef + ", Final Mark: " + finalMark[0]);
```
输出结果如下:
```plain
currentValue=null, currentMark=false
currentValue=true, currentMark=true, casResult=true
currentValue=true, currentMark=true
currentValue=true, currentMark=false, attemptMarkResult=true
currentValue=null, currentMark=false
currentValue=true, currentMark=true, wCasResult=true
Initial Reference: SnailClimb, Initial Mark: false
Update Success: true
Updated Reference: Daisy, Updated Mark: true
Update with Wrong Mark Success: false
Final Reference: Daisy, Final Mark: true
```
## 对象的属性修改类型原子类
@ -360,52 +348,48 @@ currentValue=true, currentMark=true, wCasResult=true
**`AtomicIntegerFieldUpdater` 类使用示例** :
```java
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterTest {
public static void main(String[] args) {
AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User("Java", 22);
System.out.println(a.getAndIncrement(user));// 22
System.out.println(a.get(user));// 23
}
// Person 类
class Person {
private String name;
// 要使用 AtomicIntegerFieldUpdater字段必须是 public volatile
private volatile int age;
//省略getter/setter和toString
}
class User {
private String name;
public volatile int age;
// 创建 AtomicIntegerFieldUpdater 对象
AtomicIntegerFieldUpdater<Person> ageUpdater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 创建 Person 对象
Person person = new Person("SnailClimb", 22);
public String getName() {
return name;
}
// 打印初始值
System.out.println("Initial Person: " + person);
public void setName(String name) {
this.name = name;
}
// 更新 age 字段
ageUpdater.incrementAndGet(person); // 自增
System.out.println("After Increment: " + person);
public int getAge() {
return age;
}
ageUpdater.addAndGet(person, 5); // 增加 5
System.out.println("After Adding 5: " + person);
public void setAge(int age) {
this.age = age;
}
ageUpdater.compareAndSet(person, 28, 30); // 如果当前值是 28则设置为 30
System.out.println("After Compare and Set (28 to 30): " + person);
}
// 尝试使用错误的比较值进行更新
boolean isUpdated = ageUpdater.compareAndSet(person, 28, 35); // 这次应该失败
System.out.println("Compare and Set (28 to 35) Success: " + isUpdated);
System.out.println("Final Person: " + person);
```
输出结果:
```plain
22
23
Initial Person: Name: SnailClimb, Age: 22
After Increment: Name: SnailClimb, Age: 23
After Adding 5: Name: SnailClimb, Age: 28
After Compare and Set (28 to 30): Name: SnailClimb, Age: 30
Compare and Set (28 to 35) Success: false
Final Person: Name: SnailClimb, Age: 30
```
## 参考

View File

@ -40,14 +40,18 @@ boolean compareAndSwapInt(Object o, long offset, int expected, int x);
boolean compareAndSwapLong(Object o, long offset, long expected, long x);
```
`Unsafe`类中的 CAS 方法是`native`方法。`native`关键字表明这些方法是用本地代码(通常是 C 或 C++)实现的,而不是用 Java 实现的。这些方法直接调用底层的硬件指令来实现原子操作。也就是说Java 语言并没有直接用 Java 实现 CAS,而是通过 C++ 内联汇编的形式实现的(通过 JNI 调用。因此CAS 的具体实现与操作系统以及 CPU 密切相关
`Unsafe`类中的 CAS 方法是`native`方法。`native`关键字表明这些方法是用本地代码(通常是 C 或 C++)实现的,而不是用 Java 实现的。这些方法直接调用底层的硬件指令来实现原子操作。也就是说Java 语言并没有直接用 Java 实现 CAS。
`java.util.concurrent.atomic` 包提供了一些用于原子操作的类。这些类利用底层的原子指令,确保在多线程环境下的操作是线程安全的。
更准确点来说Java 中 CAS 是 C++ 内联汇编的形式实现的,通过 JNIJava Native Interface 调用。因此CAS 的具体实现与操作系统以及 CPU 密切相关。
`java.util.concurrent.atomic` 包提供了一些用于原子操作的类。
![JUC原子类概览](https://oss.javaguide.cn/github/javaguide/java/JUC%E5%8E%9F%E5%AD%90%E7%B1%BB%E6%A6%82%E8%A7%88.png)
关于这些 Atomic 原子类的介绍和使用,可以阅读这篇文章:[Atomic 原子类总结](https://javaguide.cn/java/concurrent/atomic-classes.html)。
Atomic 类依赖于 CAS 乐观锁来保证其方法的原子性,而不需要使用传统的锁机制(如 `synchronized` 块或 `ReentrantLock`)。
`AtomicInteger`是 Java 的原子类之一,主要用于对 `int` 类型的变量进行原子操作,它利用`Unsafe`类提供的低级别原子操作方法实现无锁的线程安全性。
下面,我们通过解读`AtomicInteger`的核心源码JDK1.8),来说明 Java 如何使用`Unsafe`类的方法来实现原子操作。
@ -147,8 +151,6 @@ CAS 经常会用到自旋操作来进行重试,也就是不成功就一直循
### 只能保证一个共享变量的原子操作
CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5 开始,提供了`AtomicReference`类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用`AtomicReference`类把多个共享变量合并成一个共享变量来操作。
CAS 操作仅能对单个共享变量有效。当需要操作多个共享变量时CAS 就显得无能为力。不过,从 JDK 1.5 开始Java 提供了`AtomicReference`类,这使得我们能够保证引用对象之间的原子性。通过将多个变量封装在一个对象中,我们可以使用`AtomicReference`来执行 CAS 操作。
除了 `AtomicReference` 这种方式之外,还可以利用加锁来保证。
@ -157,4 +159,4 @@ CAS 操作仅能对单个共享变量有效。当需要操作多个共享变量
在 Java 中CAS 通过 `Unsafe` 类中的 `native` 方法实现,这些方法调用底层的硬件指令来完成原子操作。由于其实现依赖于 C++ 内联汇编和 JNI 调用,因此 CAS 的具体实现与操作系统以及 CPU 密切相关。
CAS 作为实现乐观锁的核心算法,虽然具有高效的无锁特性,但也需要注意 ABA 问题、循环时间长开销大等问题。
CAS 虽然具有高效的无锁特性,但也需要注意 ABA 、循环时间长开销大等问题。