GirlsBoy
回首向来萧瑟处,归去,也无风雨也无晴。

java作为一种跨平台、面向对象的编程语言,广泛应用于企业级Web开发和移动应用开发。其核心特性包括可移植性(一次编写,到处运行)、安全性、分布式支持以及泛型编程能力。又因其简单易学深受广大程序员所喜爱。自1995年推出以来,语言也经历了多次的迭代更新,而对于一些较早接触该语言的老程序员来说,对一些新特征甚至语言层面的优化或许并不熟悉,针对一些较为常见的语言特征,在本文中进行了较为细致的入门介绍,希望能在学习语言的过程中起到积极的作用。另外由于水平有限,对某些特性的理解或许存在偏差,如果您发现有任何问题,欢迎随时交流学习。
## 1、volatile在线程安全中能保证什么

**
通常以为,使用volatile变量就可以保证线程安全.事实真的如此吗?
其实,volatile只能保障的是可见性和排序,并不能保证操作的原子性,更不能防止竞态条件(race conditions)的发生.当一个变量被标记为volatile时,1) 某线程对该变量的任何写入操作会立即对其他线程可见;2) 读写操作不会对该变量的相关操作进行重排序.
```java
volatile int i;
i++;
```
代码中变量i虽然使用volatile修饰,但其并不是线程安全的,因为i++不是一个原子操作,它包含1 读取数值;2 加1;3 写回数值.因volatile修饰变量不能保证原子性,当两个线程同时执行上述代码,便会导致线程安全的问题.
**
## 2、是否应该选择synchronized锁*
*
长久以来的认知告诉我们,synchronized速度慢,要避免使用.选用替代方案concurrent.lock包下的作为替代方案.从jdk1.6开始,针对synchronized的优化已经显著提高了其性能,jdk1.6引入了”锁升级”机制, 将锁的状态分为四种:‌无锁 → 偏向锁 → 轻量级锁 → 重量级锁‌。JVM 会根据线程竞争情况动态升级锁,避免不必要的开销.
许多情况下, synchronized由JVM优化,比手动锁定更快,语义也更容易理解.但当多个线程争用一个synchronized锁时,速度会变慢.如下代码中,如果doSomeThing()抛出异常,会发生什么情况?
```java
synchronized (lock) {
doSomeThing();
}
```
锁会自动释放!
同样如果使用手动锁,则必须注意锁释放.如使用ReentrantLock,需注意解锁某个finally区域,否则,一旦异常该区域将永远保持锁定状态,出现死锁.
```java
lock.lock ();
try {
doSomeThing ();
} finally {
lock.unlock () ;
}
```
**
## 3、可以使用Thread.sleep()协调线程吗

答案是否定的.因为Thread.sleep()并不能保证时间,不能协调状态,会在机器低速运转或重载时发生故障.想要正确的协调线程,需使用:
1、wait()/notify();
2、CountDownLatch;
3、CyclicBarrier;
4、CompletebleFuture.
那么,wait()和sleep()之间有什么区别呢?
其区别主要包括
1、sleep()不释放锁,线程将占用cpu资源,wait()会释放监视器锁,
2、sleep()可以在任何地方使用,而wait()必须在synchronized代码块/方法中使用
3、sleep()作用是暂停执行(休眠),而wait()作用是线程间通信
4、sleep()的唤醒条件是超时自动唤醒,而wait()需等待notify()或notifyAll()
用一个日常生活中的场景大致描述一下,sleep()类似于你在床上看书,看累了抱着书躺下睡觉,虽然你不再看书,但因为书被你抱着,其他人是没法去看你手中的书.wait()类似你去排队出地铁站,在出站前你发现手机支付地铁票有问题,于是你让出排队通道去修复地铁卡手机支付,其他人依次跟上排队出站,等你解决了支付问题,又重新排进了出站队伍中.
在系统中,特别是多线程系统中使用sleep()只会降低系统性能.
**
## 4、多线程环境中,双重检查一定是安全的吗?

**
以下代码,在多线程环境中,是否存在问题?
```java
public class MyClass {
private static MyClass instance;
public static MyClass getInstance() {
if (instance == null) {
synchronized (MyClass.class) {
if (instance == null) {
instance = new MyClass();
}
}
}
return instance;
}
}
```
答案是肯定的。问题的根本是因为指令重排的存在,而JVM在执行instance = new MyClass();这行代码时,其操作并不是原子的,通常分为三个步骤: •
• 1.分配内存空间:为对象分配一块内存。
• 2.初始化对象:在内存中构造对象(执行构造函数)。
• 3.引用赋值:将 instance 变量指向刚才分配的内存地址。
而JVM和CPU为了优化性能,这三个步骤并不是严格的顺序执行的.这便可能导致另一个线程可能会看到一个半初始化的对象.而修复此问题便可以通过使用添加volatile修饰禁止其重排序修复后代码如下
```java
public class MyClass {
// 必须增加 volatile 关键字
private static volatile MyClass instance;

public static MyClass getInstance() {
if (instance == null) {
synchronized (MyClass.class) {
if (instance == null) {
instance = new MyClass();
}
}
}
return instance;
}
}
```
**
## 5、Java对象创建经历哪些阶段?

**
Java对象创建的五个阶段——类加载检查、内存分配、零值初始化、对象头设置和执行<init>方法——在逻辑上是顺序执行的。这些步骤共同构成了Java虚拟机(JVM)实例化对象的过程,按照由前到后的顺序保证了对象在内存中的正确配置和初始化。
具体细节如下:
• 1. 类加载检查:虚拟机检查new指令的参数能否在常量池中定位到一个类的符号引用,并检查该类是否已加载、解析和初始化。
• 2. 内存分配:在类加载检查通过后,虚拟机为对象在堆中分配内存。
• 3. 零值初始化:将分配的内存空间初始化为零值(不包含对象头),保证字段在该阶段就能使用默认值。
• 4. 对象头设置:设置对象头,包括哈希码、GC分代年龄、锁状态标志等。
• 5. 执行<init>方法:执行开发者定义的构造函数,按照程序员的意愿对对象进行初始化。

posted on 2026-01-23 22:08  GirlsBoy  阅读(0)  评论(0)    收藏  举报

java\web应用开发&研究

梦想程序改变生活

成为一个了不起的人