JUC

117 阅读8分钟

进程线程协程

img_v3_028i_ae2338c8-a716-44e6-9db2-4dbb33e1ebdg.jpg

线程的生命周期和状态

f6a2ef62-9137-4f1f-9649-afc2f06e772e.jpeg

线程的上下文切换

c43869b7-dcdb-4f57-b9a2-7cc415c37851.jpeg

并发安全的三大特性

7b22fa88-56f3-40d7-b607-3e90cb6eba65.jpeg

JMM

qapUShuzV9.jpg 每个线程中都有共享变量的本地副本

happends-before

815cf34f-4475-4ea2-b238-d7a8dde13a00.jpeg

0c0b36f9-c73a-4282-a919-d8aca4e516c4.jpeg

CAS

34a11fcf-5fe0-414f-83e5-b694c56ee526.jpeg 三大问题

e2bad3eb-d5eb-45d4-8ec8-230707d4f5f5.jpeg

AQS

mDJK06pHEj.jpg

ReentrantLock源码

77906568-ab4a-4c50-b442-496bd9daf755.jpeg

ehuNt1wrX7.jpg

75ad6fe9-a1f1-49fc-b5d9-b86db9788ec4.jpeg

sleep()和wait()方法、yield()、join()方法

aae8d75c-93a8-4bb7-8c85-3794e6657bbb.jpeg

为什么wait()不定义在Thread中,sleep()为什么定义在Thread中

9579404a-5ecf-4482-afe4-4a7791228fe2.jpeg

Lock接口与synchronized

740e2fe6-9835-462a-86a8-f59b6a89f894.jpeg

volatile关键字

ac69dbc5-f661-4df1-b21e-947fd3b156ce.jpeg

 * 双重检查方式
 */
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关键字

是什么?

RvQlzfuKmq.jpg 锁是什么?

CPfXPnwVfc.jpg 底层原理?

SidKke9d1l.jpg 锁升级机制?

CjoCwVy3IV.jpg JDK1.6后的底层优化

TIVFHd4Xki.jpg

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()方法。

线程池

好处:

f0792759-08a2-4b78-8114-24b5f4c18325.jpeg 原理:

dd866175-6f9e-4723-b364-4499136f4920.jpeg

参数

06670b63-882a-4fe2-aa7e-74129ef1159a.jpeg 饱和策略:

34c74d0b-bd97-430a-9a02-924c35eb254a.jpeg 线程复用的原理

img_v3_028j_acb1eece-a2e5-48a0-a5d2-38975d78b4bg.jpg 几种常见线程池

f597ff84-41e0-465f-8981-92a391a891cc.jpeg

5855d83d-56d2-46d9-9bf4-8257f98d5e95.jpeg

PhP4r8Qf2l.jpg

c4mKPKNsuB.jpg 为什么先添加队列而不是先创建最大线程 oSteBkwBk8.jpg

如何创建线程

5d43b71b-82c9-4140-b1ca-90a1c57dfb05.jpeg

433ce195-a3e7-48dc-ab21-386154d695da.jpeg

Atomic原子类?

介绍?

Atomic原子类是Java中提供的一组原子操作类,可以保证对于特定变量的操作具有原子性,即对于多线程并发访问同一个变量时,可以保证操作的原子性,不会出现数据不一致的问题。

e898550d-6132-43c5-b413-0a09d7342b87.jpeg img_v3_028i_ae2338c8-a716-44e6-9db2-4dbb33e1ebdg.jpg Java中提供了多种Atomic原子类,例如AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference等,它们分别对应着不同类型的变量,可以进行不同类型的原子操作。

Atomic原子类的实现原理是利用了CAS(Compare And Swap)算法,即比较并交换。CAS算法是一种无锁算法,可以在多线程并发访问时保证数据的原子性,它通过比较内存中的值和期望值是否相等来判断变量是否被修改过,如果没有被修改过,则进行修改,否则重新进行比较,直到修改成功为止。

使用Atomic原子类可以避免使用synchronized关键字或者Lock锁来保证数据的同步,从而提高程序的并发性能和效率。

应用场景

原子类的应用场景主要是在多线程并发访问共享变量时,保证对变量的操作具有原子性,避免出现数据不一致的问题。以下是一些常见的应用场景:

  1. 计数器:在多线程环境下对某个计数器进行增减操作时,可以使用AtomicInteger保证操作的原子性,避免出现并发问题。

  2. 缓存控制:在使用缓存时,可以使用AtomicReference来保证缓存的原子性,避免出现脏数据问题。

  3. 状态标记:在多线程环境下,可以使用AtomicBoolean来表示某个状态的标记,避免出现并发问题。

  4. 序列号生成器:在生成序列号时,可以使用AtomicLong来保证序列号的唯一性和原子性。

  5. 数据库乐观锁:在实现数据库乐观锁时,可以使用AtomicReference或AtomicStampedReference来保证数据的版本号的原子性。

总之,当需要在多线程环境下对某个共享变量进行操作时,可以考虑使用Atomic原子类来保证操作的原子性,从而避免出现并发问题。

代码示例

下面是一些常见应用场景的代码示例:

  1. 计数器
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();
    }
}
  1. 缓存控制
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();
    }
}
  1. 状态标记
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
        }
    }
}
  1. 序列号生成器
import java.util.concurrent.atomic.AtomicLong;

public class SeqGenerator {
    private AtomicLong seq = new AtomicLong(0);

    public long getNextSeq() {
        return seq.incrementAndGet();
    }
}
  1. 数据库乐观锁
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有以下优点:

  1. 异步编程:CompletableFuture可以很方便地处理异步操作,不需要使用回调函数或者线程池等繁琐的操作。

  2. 可组合性:CompletableFuture提供了很多方法来支持串行和并行操作,可以将多个异步操作组合在一起,形成一个异步处理流程。

  3. 异常处理:CompletableFuture可以很方便地处理异步操作中可能出现的异常,提供了一些方法来处理异常情况。

  4. 可取消性:CompletableFuture可以很方便地取消异步操作,提供了一些方法来取消异步操作。

  5. 非阻塞式:CompletableFuture的操作是非阻塞式的,可以提高程序的性能和效率。

  6. 可以处理复杂异步操作: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时,表示所有的线程都已执行完毕,然后在等待的线程就可以恢复执行任务。