1.JVM对sychronized的优化:锁膨胀、锁消除、锁粗化、自适应自旋锁
(1)锁膨胀:从无锁、偏向锁、轻量级锁、重量级锁的过程叫做锁膨胀。在JDK1.6以前,sychronized是由重量级锁实现的,加锁和解锁的过程需要从用户态切换到内核态,性能低下。有了锁膨胀机制,大多数场景可以使用无锁、偏向锁和轻量级锁,在执行并发操作的时候,避免了线程从用户态到内核态,从而减少性能的开销。
(2)锁消除:JVM检测到代码段不再被共享和竞争,此时就会取消该代码段的所属的代码段。
(3)锁粗化:将多个连续的加锁、解锁操作链接在一起,形成一个范围更大的锁。
(4)自适应自旋锁:通过自身循环,获取锁的一种方式,避免了线程开启和线程挂起的性能开销。因为线程开启和线程挂起需要从用户态转到内核态,这个过程是比较缓慢的,性能比较低下。
2.介绍AQS
3.CAS和AQS的关系
4.用AQS实现可重入的公平锁
5.ThreadLocal的作用
6.乐观锁和悲观锁
7.java中实现乐观锁的方式
8.CAS的缺点
9.为什么不能所有的锁都用CAS
10.CAS有什么问题,java是如何解决的
11.volatile的作用
volatile可以保证可见性,不能保证原子性,所以会引发线程安全的问题。如果一个线程修改了使用volatile关键字的修饰的变量,其他线程也能获取到这个变量的最新值,从而避免了数据不一致的状态。
对于复合操作,比如i++这种自增操作,因为不是原子操作,如果有多个线程修改了i的值,volatile是不能保证线程安全的。需要用Sychronized和Lock来保证原子性和线程安全。
12.volatile和sychronized的比较
13.什么是公平锁和非公平锁。
14.非公平锁的吞吐量为什么比公平锁大。
15.reentrantlock是怎么实现公平锁的。
公平锁和非公平锁的区别是,公平锁中有hasQueueProcessors()=false方法,来看自己之前还有线程在排队吗
hasQueuedPredecessors() 用来判断 当前等待队列是否有线程在排队获取锁。对于非公平锁,无论是否已经有线程在排队,都会尝试获取锁,获取不到再排队。
tryLock()方法是非公平的,可以插队
16.线程池的核心参数
new SynchronousQueue<>() 是 Java 并发包 里的一个用法。SynchronousQueue 是 java.util.concurrent 提供的一种特殊的 阻塞队列。
和普通的队列(ArrayBlockingQueue、LinkedBlockingQueue)不同:它不存储任何元素,容量是 0。
每次 put() 必须等到有另一个线程 take(),才能成功;反过来 take() 也必须等到有人 put()。
所以它其实是一个 线程之间直接移交(handoff)数据的工具。
把 SynchronousQueue 想象成 过手交易:
线程 A 想交给线程 B 一个包裹。
A 必须等到 B 伸手来拿(调用 take()),才能把东西放出去。
它不像普通队列能"先存进去,等别人慢慢取"。
17.await()方法
(1)Condition.await()
在 java.util.concurrent.locks.Condition 接口中,await() 是用来 让当前线程等待,直到其他线程通过 signal() 或 signalAll() 唤醒它。常用场景是自定义锁下的线程等待/通知机制。
java
import java.util.concurrent.locks.*;
public class AwaitExample {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
lock.lock();
try {
System.out.println("Thread t1: Waiting...");
condition.await(); // 等待被唤醒
System.out.println("Thread t1: Woke up!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
Thread t2 = new Thread(() -> {
lock.lock();
try {
System.out.println("Thread t2: Sleeping for 2s...");
Thread.sleep(2000);
condition.signal(); // 唤醒等待的线程
System.out.println("Thread t2: Sent signal!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
}
}
await() 会释放锁并让当前线程等待。
signal() 或 signalAll() 会唤醒等待线程。
与 Object.wait() 类似,但更灵活,可以在 Lock 中使用。
(2)CountDownLatch.await()
在 java.util.concurrent.CountDownLatch 中,await() 是 阻塞当前线程,直到计数器为 0。
java
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " finished work");
latch.countDown(); // 计数器减 1
}).start();
}
latch.await(); // 阻塞,直到计数器为 0
System.out.println("All threads finished!");
(3)CompletableFuture.await()(类似概念) 不常用
在 异步编程里,比如在某些库或 Kotlin/JavaScript 的 async/await 中,await 用来 等待异步操作完成并获取结果。Java 标准库里 CompletableFuture 没有 await() 方法,但可以用 get() 或 join() 实现类似效果。
java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
String result = future.join(); // 等待结果
System.out.println(result); // 输出 "Hello"
18.例子3
join() → 一直等,直到目标线程结束。
前面例子里写的 t1.join(); t2.join(); 的意思就是:
主线程会先等待 t1 执行结束,再等待 t2 执行结束,最后才继续往下走。这样可以确保两个子线程都跑完。
假设两个线程并发读写同一个整型变量,初始值为零,每个线程加 50次,结果可能是什么?
在没有任何同步机制的情况下,两个线程并发对同一个整型变量进行 50 次加1操作,最终结果可能是100,也可能小于 100,最坏的结果是 50,也就是最终的结果可能是在 [50,100]。
小于 100 情况的分析,由于对整型变量的 num++ 操作不是原子操作,它实际上包含了三个步骤:读取变量的值、将值加 1、将新值写回变量。在多线程环境下,可能会出现线程安全问题。例如,线程1和线程2同时读取了变量的当前值,然后各自将其加 1,最后都将相同的新值写回变量,这就导致了一次加 1操作的丢失。这种情况会多次发生,最终结果就会小于 100。
java
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerAddition {
private static AtomicInteger num = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException{
Thread thread1 = new Thread(()->{
for(int i=0;i<50;i++){
num.incrementAndGet();}
});
Thread thread2 = new Thread(()-> {
for(int i=0;i<50;i++){
num.incrementAndGet();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("最终结果:"+ num.get());
}
}
通过sychronized方法,保证操作的互斥性。
java
public class SynchronizedAddition {
private static int num =0;
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()-> {
for(int i=0;i<50;i++){
synchronized(lock){
num++;
}
}
});
Thread thread2 = new Thread(()-> {
for (int i = 0; i < 50; i++) {
synchronized (lock) {
num++;
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("最终结果:"+ num);
}
}