进程线程协程
线程的生命周期和状态
线程的上下文切换
并发安全的三大特性
JMM
每个线程中都有共享变量的本地副本
happends-before
锁
CAS
三大问题
AQS
ReentrantLock源码
sleep()和wait()方法、yield()、join()方法
为什么wait()不定义在Thread中,sleep()为什么定义在Thread中
Lock接口与synchronized
volatile关键字
* 双重检查方式
*/
public class Singleton {
//私有构造方法
private Singleton() {}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
if(instance == null) {
synchronized (Singleton.class) {
//抢到锁之后再次判断是否为null
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
该关键字很有必要,instance = new Singleton()分三步执行1.为instance分配内存空间 2初始化instance 3将instace指向分配的地址内存。
由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,
但是在多线程环境下会导致一个线程获得还没有初始化的实例。
例如,线程 T1 执行了 1 和 3,此时 T2 调用 getInstance() 后发现 Instance 不为空,因此返回 Instance,但此时 Instance 还未被初始化。
synchronized关键字
是什么?
锁是什么?
底层原理?
锁升级机制?
JDK1.6后的底层优化
ThreadLocal
每个线程都有自己的专属本地变量,不能被其他线程访问修改,ThreadLocal可以比作存数据的盒子,盒子可以存储每个线程的私有数据。创建一个ThreadLocal变量,访问这个变量的每个线程都会有这个变量的本地副本。
可以使用 get() 和 set() 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。
原理:变量是放在了当前线程的 ThreadLocalMap 中,并不是存在 ThreadLocal上, ThreadLocal 可以理解为只是ThreadLocalMap的封装,传递了变量值。
ThrealLocal 类中可以通过Thread.currentThread()获取到当前线程对象后,直接通过getMap(Thread t)可以访问到该线程的ThreadLocalMap对象。
每个Thread中都有一个ThreadLocalMap,而ThreadLocalMap可以存储以ThreadLocal为 key ,Object 对象为 value 的键值对。
默认情况下该map为null只有当前线程调用 ThreadLocal 类的 set或get方法时才创建它们,调用这两个方法的时候,调用的是ThreadLocalMap类对应的 get()、set()方法。
内存泄漏问题:ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,而 value 是强引用。如果 ThreadLocal 没有被外部强引用的情况下,在GC,key 会被清理掉,而 value 不会被清理掉。
,ThreadLocalMap 中就会出现 key 为 null 的 Entry。如果不管,value 永远无法被 GC 回收,可能会产生内存泄露。
ThreadLocalMap 实现中考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。使用完 ThreadLocal方法后 最好手动调用remove()方法。
线程池
好处:
原理:
参数
饱和策略:
线程复用的原理
几种常见线程池
为什么先添加队列而不是先创建最大线程
如何创建线程
Atomic原子类?
介绍?
Atomic原子类是Java中提供的一组原子操作类,可以保证对于特定变量的操作具有原子性,即对于多线程并发访问同一个变量时,可以保证操作的原子性,不会出现数据不一致的问题。
Java中提供了多种Atomic原子类,例如AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference等,它们分别对应着不同类型的变量,可以进行不同类型的原子操作。
Atomic原子类的实现原理是利用了CAS(Compare And Swap)算法,即比较并交换。CAS算法是一种无锁算法,可以在多线程并发访问时保证数据的原子性,它通过比较内存中的值和期望值是否相等来判断变量是否被修改过,如果没有被修改过,则进行修改,否则重新进行比较,直到修改成功为止。
使用Atomic原子类可以避免使用synchronized关键字或者Lock锁来保证数据的同步,从而提高程序的并发性能和效率。
应用场景
原子类的应用场景主要是在多线程并发访问共享变量时,保证对变量的操作具有原子性,避免出现数据不一致的问题。以下是一些常见的应用场景:
-
计数器:在多线程环境下对某个计数器进行增减操作时,可以使用AtomicInteger保证操作的原子性,避免出现并发问题。
-
缓存控制:在使用缓存时,可以使用AtomicReference来保证缓存的原子性,避免出现脏数据问题。
-
状态标记:在多线程环境下,可以使用AtomicBoolean来表示某个状态的标记,避免出现并发问题。
-
序列号生成器:在生成序列号时,可以使用AtomicLong来保证序列号的唯一性和原子性。
-
数据库乐观锁:在实现数据库乐观锁时,可以使用AtomicReference或AtomicStampedReference来保证数据的版本号的原子性。
总之,当需要在多线程环境下对某个共享变量进行操作时,可以考虑使用Atomic原子类来保证操作的原子性,从而避免出现并发问题。
代码示例
下面是一些常见应用场景的代码示例:
- 计数器
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
- 缓存控制
import java.util.concurrent.atomic.AtomicReference;
public class Cache {
private AtomicReference<Object> cache = new AtomicReference<>(null);
public void setCache(Object obj) {
cache.set(obj);
}
public Object getCache() {
return cache.get();
}
}
- 状态标记
import java.util.concurrent.atomic.AtomicBoolean;
public class Worker {
private AtomicBoolean isWorking = new AtomicBoolean(false);
public void start() {
if (isWorking.compareAndSet(false, true)) {
// do something
}
}
public void stop() {
if (isWorking.compareAndSet(true, false)) {
// do something
}
}
}
- 序列号生成器
import java.util.concurrent.atomic.AtomicLong;
public class SeqGenerator {
private AtomicLong seq = new AtomicLong(0);
public long getNextSeq() {
return seq.incrementAndGet();
}
}
- 数据库乐观锁
import java.util.concurrent.atomic.AtomicStampedReference;
public class User {
private String name;
private int age;
private AtomicStampedReference<Integer> version = new AtomicStampedReference<>(0, 0);
public void update(int newAge) {
int oldVersion = version.getStamp();
if (version.compareAndSet(age, newAge, oldVersion, oldVersion + 1)) {
age = newAge;
} else {
throw new IllegalStateException("Concurrent update detected");
}
}
}
以上示例代码仅供参考,实际使用时需要根据具体的业务需求进行调整。
CompletableFuture?
介绍?
CompletableFuture是Java 8引入的一种异步编程的工具类,它可以很方便地处理异步操作。它提供了一种简单的方式来处理异步操作,并且可以很方便地进行串行和并行操作。
CompletableFuture可以看作是Java 8中的Future的增强版,它可以更好地支持异步编程。它提供了很多方法来处理异步操作,例如thenApply、thenAccept、thenCompose等等。这些方法可以将异步操作串联起来,形成一个异步的处理流程。
同时,CompletableFuture也提供了一些方法来支持并行操作,例如allOf、anyOf等等。CompletableFuture的优点在于它可以很方便地处理异步操作,同时提供了很多方法来支持串行和并行操作。它也可以很方便地处理一些复杂的异步操作,例如等待多个异步操作完成后再进行下一步操作等等。
优点?
Java中的CompletableFuture有以下优点:
-
异步编程:CompletableFuture可以很方便地处理异步操作,不需要使用回调函数或者线程池等繁琐的操作。
-
可组合性:CompletableFuture提供了很多方法来支持串行和并行操作,可以将多个异步操作组合在一起,形成一个异步处理流程。
-
异常处理:CompletableFuture可以很方便地处理异步操作中可能出现的异常,提供了一些方法来处理异常情况。
-
可取消性:CompletableFuture可以很方便地取消异步操作,提供了一些方法来取消异步操作。
-
非阻塞式:CompletableFuture的操作是非阻塞式的,可以提高程序的性能和效率。
-
可以处理复杂异步操作:CompletableFuture可以很方便地处理一些复杂的异步操作,例如等待多个异步操作完成后再进行下一步操作等等。
综上所述,Java中的CompletableFuture具有非常强大的异步处理能力,可以很方便地处理异步操作,提高程序的性能和效率。
代码示例
public class CompletableFutureExample {
public static void main(String[] args) throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, CompletableFuture!";
});
future.thenAccept(result -> System.out.println(result));
System.out.println("CompletableFuture example");
Thread.sleep(2000);
}
}
CountDownLatch
什么是?
CountDownLatch是一个同步工具类,它通过一个计数器来实现的,初始值为线程的数量。每当一个线程完成了自己的任务,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已执行完毕,然后在等待的线程就可以恢复执行任务。