Qouson's blog Qouson's blog
首页
  • Java 基础

    • 基础
    • String
  • Java 中级

    • 网络编程
  • Java 高级

    • JVM
    • 多线程
  • Spring
  • SpringMVC
  • SpringBoot
  • MySQL
  • Redis
  • MQ
  • ZooKeeper
  • git
  • linux
  • 设计模式
  • 数据结构与算法
  • 计算机基础
  • Java相关框架
  • 分布式
  • DDD领域驱动设计
  • 系统设计
  • 杂乱无章
Java知识图谱
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

qouson

Java界的小学生
首页
  • Java 基础

    • 基础
    • String
  • Java 中级

    • 网络编程
  • Java 高级

    • JVM
    • 多线程
  • Spring
  • SpringMVC
  • SpringBoot
  • MySQL
  • Redis
  • MQ
  • ZooKeeper
  • git
  • linux
  • 设计模式
  • 数据结构与算法
  • 计算机基础
  • Java相关框架
  • 分布式
  • DDD领域驱动设计
  • 系统设计
  • 杂乱无章
Java知识图谱
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • String

  • 网络编程

  • JVM

  • 多线程

    • 多线程-线程基础
    • 多线程-AQS
    • 多线程-锁
    • JUC
      • 线程
        • 线程状态
        • 新启动线程的方式
        • 有T1,T2,T3三个线程,怎样保证T2在T1后,T3在T2后
      • JMM,Java内存模型
        • 内存可见性
        • 原子性
        • 有序性
      • 等待/通知机制
        • Thread
        • Object
        • Lock.Condition
        • LockSupport
        • 范式
      • CompeletableFuture
      • volatile
        • volatile是JVM提供的轻量级的同步机制
        • 在哪些地方用过volatile
      • CAS(比较并交换)
      • synchronized
        • monitor机制
        • 锁对象
        • 内存结构
        • jvm对象描述结构
        • 作用域
        • 锁对象转换
        • synchronied和Lock区别
        • AQS(AbstractQueuedSynchronizer)
        • Lock
        • ReentrantLock
        • ReentrantReadWriteLock
        • StampedLock
        • CountDownLatch/CyclicBarrier/Semaphore
        • CountDownLatch
        • CyclicBarrier
        • Semaphore
        • 线程池
        • 线程池的种类
        • ThreadPoolExecutor
        • ThreadPoolExecutor执行过程
        • 线程池死锁
      • ThreadLocal
        • 使用
        • 给每个线程提供一个独立副本,与线程强关联,跨方法进行参数传递
        • 为什么用
        • 实现
        • 用例
        • 问题
  • JavaSE
  • 多线程
qouson
2024-05-31
目录

JUC

# JUC(java.util.concurrent)

# 线程

# 线程状态

  • NEW(新建):线程对象被创建后,但还没有调用start()方法
  • RUNNABLE(可运行):线程对象被创建后,调用start()方法,线程处于就绪状态
  • READY(就绪):线程对象被创建后,调用start()方法,线程处于就绪状态
  • RUINNING(运行):线程正被操作系统的调度执行,此时若调用yied()方法,只是谦让出时间片,具体是否让出取决于cpu
  • BLOCKED(阻塞):线程正在等待锁的释放,以便进入同步代码块/方法
  • WATING(等待):线程处于等待状态,调用wait()方法或者LockSupport.park()方法,进入等待池,等待其他线程调用notify()方法唤醒
  • TIME_WAITING(超时等待):线程调用了带有时间限制的Threed.sleep(long)或者Object.wait(long),或者Thread.join(long)或者LockSupport.parkNanos(long)或者LockSupport.parkUntil(long),进入超时等待,等待指定时间后自动唤醒
  • TERMINATED(终止):线程执行完毕或因异常而结束,不再处于活动状态

# 新启动线程的方式

  • 继承Thread类,重写run()方法
  • 实现Runnable接口,重写run()方法
  • 实现Callable接口,重写call()方法
  • 线程池

官方说法Java中有两种方式来创建线程,一种是继承Thread类,另一种是实现Runnable接口。

# 有T1,T2,T3三个线程,怎样保证T2在T1后,T3在T2后

  • join():把指定线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行,比如在线程B中调用了线程A的join()方法,直到线程A执行完毕后才会继续执行线程B剩下的代码
  • 用Thread#join
  • 在T3中调用T2的join()方法
  • 在T2中调用T1的join()方法

# JMM,Java内存模型

mian内存:所有线程共享的内存,包括堆内存和栈内存 线程私有内存:每个线程都有自己私有的内存,包括程序计数器、虚拟机栈和本地方法栈 线程执行时,会从主内存中读取数据到线程私有内存中,执行后将结果写入到主内存中

# 内存可见性

  • 可见性:一个线程修改了共享变量的值,其他线程能够立即看到修改的值

# 原子性

  • 原子性:一个操作或多个操作,要么全部执行,要么全部不执行

# 有序性

  • 有序性:程序执行的顺序按照代码的先后顺序执行

# 等待/通知机制

# Thread

  • Thread.yied():让出时间片,让其他线程运行,不会释放锁
  • Thread.sleep(long):休眠指定时间,不会释放锁
  • Thread.join():等待其他线程执行完毕,不会释放锁

# Object

  • 线程A调用了对象O的wait()方法,另一个线程B调用了对象O的notify()方法或者notifyAll(),线程A收到通知后从对象O的wait()方法返回
  • 等待,Object.wait(),释放当前线程持有的锁,唤醒后重新抢锁
  • 通知,Object.notifyAll()
  • 强制在同步块中调用,否则抛出IllegalMonitorStateException

# Lock.Condition

  • condition.await():释放当前线程持有的锁,唤醒后重新抢锁
  • conditon.signal():唤醒一个等待的线程

# LockSupport

  • LockSupport.park():释放当前线程持有的锁,唤醒后重新抢锁
  • 调用LockSupport.unpark(Thread)方法,唤醒指定线程

# 范式

  • 等待方
加锁(对象){
while(条件不满足){
对象.wait()
}
进入后面的业务逻辑
}
1
2
3
4
5
6
  • 通知方
加锁(对象){
加锁(对象){
业务逻辑,改变条件
对象.notify()
}
1
2
3
4
5

# CompeletableFuture

  • 使用Runable或Callable实例来提交任务
  • 调用get()方法,阻塞等待任务执行完成
  • 调用join()方法,阻塞等待任务执行完成
  • runAsyn()方法,提交任务后立即返回void
  • supplyAsync()方法,提交任务后立即返回值
  • anyOf()方法,多个任务中任意一个执行完成就返回
  • allOf()方法,多个任务全部执行完成才返回

# volatile

# volatile是JVM提供的轻量级的同步机制

  • 保证可见性
  • 禁止指令重排
  • 不保证原子性

# 在哪些地方用过volatile

  • 单例模式DCL代码
public class Singleton{
    private volatile static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance(){
        if (null == instance){
            synchronized(Singleton.class){
                if(null == instaince){
                    instance = new Singleton();
                }
           }
       }
       return instance;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# CAS(比较并交换)

  • 预期值(expected value),当前值(current value,内存中的值),新值(new value),检测当前值是否与预期值相同,如果是,则将当前值更新为新值。compareAndSet(originalValue=expected Value,newValue)
  • 底层原理,UnSafe类中的compareAndSwapInt()方法
  • CAS的缺点
    • 循环时间长,开销大
    • 只能保证一个共享变量的原子操作
    • 会引出ABA问题

# synchronized

# monitor机制

ObjectMonitor() {
    _header       = NULL; //对象头  markOop
    _count        = 0; 
    _waiters      = 0,   
    _recursions   = 0;   // 锁的重入次数
    _object       = NULL;  //存储锁对象
    _owner        = NULL;  // 标识拥有该monitor的线程(当前获取锁的线程)
    _WaitSet      = NULL;  // 等待线程(调用wait)组成的双向循环链表,_WaitSet是第一个节点
    _WaitSetLock  = 0 ;   
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ; //多线程竞争锁会先存到这个单向链表中 (FILO栈结构)
    FreeNext      = NULL ;
    _EntryList    = NULL ; //存放在进入或重新进入时被阻塞(blocked)的线程 (也是存竞争锁失败的线程)
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 锁对象

# 内存结构

  • 对象头
    • Mark Word(存储对象的HashCode,分代年龄和锁标志位信息)
    • Class Pointer(指向对象的类元数据,虚拟机通过这个指针来确定这个对象是哪个类的实例)
    • Moenitor(指向对象的锁)
      • EntryList
      • Owner(会指向持有Monitor对象的线程)
      • WaitSet
  • 实例数据
  • 填充数据

# jvm对象描述结构

  • 32位
  • 64位

# 作用域

  • 方法:ACC_SYNCHRONIZED
  • 代码块:monitorenter和monitorexit

# 锁对象转换

  • 无锁
  • 偏向锁
    • Mark Word中有线程信息cas比较
    • 升级,
      • 没有位置保存hashCode,所以在其中调用对象的hashCode()方法会导致锁撤销
        • 处于可偏向的时候,调用obj.hashCode(),Mark Word将变成未锁定状态,升级为轻量级锁
        • 处于已偏向的时候,调用obj.hashCode(),升级为重量级锁
      • notify/wait
        • obj.notify(),升级为轻量级锁
        • obj.wait(),升级为重量级锁
    • 锁撤销
  • 轻量级锁
    • 复制了一份Mark Word到线程栈中,CAS比较
    • 轻微竞争场景
    • 同一时间多个线程竞争锁,升级为重量级锁
  • 重量级锁
    • 复制一份Mark Word到Monitor中,指向monitor对象

# synchronied和Lock区别

  • synchronied是jvm实现的锁,Lock是JDK实现的锁
  • synchronied是自动释放锁,Lock需要手动释放
  • synchronied是非公平锁,Lock可以设置公平锁
  • synchronied锁住方法和代码块,Lock锁住代码块

# AQS(AbstractQueuedSynchronizer)

  • AQS
  • CLH
  • 变种CLH,将每条请求共享变量的线程封装成一个节点
  • volatile int state
  • 变种的CLH双向队列

# Lock

# ReentrantLock

  • NonfairSync
    • tryAcquire
    • acquireQueued
    • CAS
    • 可能会授予刚刚请求它的线程,而不考虑等待时间
  • FairSync
    • hasQueuedPredecessors
    • 会授予等待时间最长的线程

# ReentrantReadWriteLock

  • ReadLock
  • WriteLock

# StampedLock

# CountDownLatch/CyclicBarrier/Semaphore

# CountDownLatch

  • 只有一个构造方法 只会被赋值一次
  • 没有别的方法可以修改 count
  • 让一些线程阻塞直到另一些线程完成操作后才被唤醒(班长关门)
  • 主要有两个方法,await方法会被阻塞。countDown会让计数器-1,不会阻塞。将计数器变为0时,调用await方法的线程会被唤醒,继续执行。
  • 相当于CompletableFuture.allOf()
  • CountDownLatchDemo
public class CountDownLatchDemo {
    //6个同学陆续离开教室之后,班长锁门
    public static void main(String[] args) throws InterruptedException {

        //创建CountDownLatch对象,设置初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);

        //6个同学陆续离开教室之后
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 号同学离开了教室");

                //计数  -1
                countDownLatch.countDown();

            },String.valueOf(i)).start();
        }

        //等待
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName()+" 班长锁门走人了");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# CyclicBarrier

  • CyclicBarrie字面上就是可循环使用的屏障。当一组线程得到一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会打开,所有被屏障拦截的线程才会继续工作。进入屏障通过await方法。
  • CyclicBarrierDemo
//集齐7颗龙珠就可以召唤神龙
public class CyclicBarrierDemo {

    //创建固定值
    private static final int NUMBER = 7;

    public static void main(String[] args) {
        //创建CyclicBarrier
        CyclicBarrier cyclicBarrier =
                new CyclicBarrier(NUMBER,()->{
                    System.out.println("*****集齐7颗龙珠就可以召唤神龙");
                });

        //集齐七颗龙珠过程
        for (int i = 1; i <=7; i++) {
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+" 星龙被收集到了");
                    //等待
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# Semaphore

  • 信号量主要用于两个目的,一个是多个共享资源的互斥使用,一个是并发线程数的控制。
  • SemaphoreDemo
//6辆汽车,停3个车位
public class SemaphoreDemo {
    public static void main(String[] args) {
        //创建Semaphore,设置许可数量
        Semaphore semaphore = new Semaphore(3);

        //模拟6辆汽车
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                try {
                    //抢占
                    semaphore.acquire();

                    System.out.println(Thread.currentThread().getName()+" 抢到了车位");

                    //设置随机停车时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));

                    System.out.println(Thread.currentThread().getName()+" ------离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 线程池

# 线程池的种类

  • newFixedThreadPool
  • newCacheThreadPool
  • newSIngleTheadExecutor
  • newScheduledThewadPool
  • newWorkStealingPool
  • ThreadPoolExecutor

# ThreadPoolExecutor

  • 核心线程数
  • 最大线程数
  • 空闲时间
  • 空闲时间单位
  • 缓冲队列
    • LinkedBlockingQueue,无界 当心内存溢出
    • ArrayBlockingQueue,有界队列,加锁保证安全 一直死循环阻塞 队列不满就唤醒
    • Synchronous
  • 工厂方法
  • 拒绝策略
    • 抛异常:AbortPolicy(默认):直接抛出RejectedException。
    • 丢弃:DiscardPolicy:直接丢弃任务,不做任何处理。
    • 丢弃最早提交的:DiscardOldestPolicy:丢弃等待最久的任务。
    • 而是回退给调用者:CallerRunsPolicy:不会抛弃任务也不会抛出异常,而是回退给调用者

# ThreadPoolExecutor执行过程

核心线程->队列->最大线程->拒绝策略

# 线程池死锁

  • 概念:使用线程池处理任务的时候,由于任务之间相互依赖,资源争用或者其他同步问题,导致所有线程都在等待彼此释放资源而无法继续执行的一种状态。

# ThreadLocal

# 使用

  • set()
  • get()
  • remove()

# 给每个线程提供一个独立副本,与线程强关联,跨方法进行参数传递

# 为什么用

  • 每个线程都保存一份数据,不会造成数据污染

# 实现

  • 设想1:synchronized map(thread,T)
  • 实际实现:不只一个T,ThreadLoalMap,定义在ThreadLocal,从Thread声明ThreadLocal.ThreadLocalMap

# 用例

  • jdbc原生
  • 连接池,事务,多个sql可能不在同一个连接,解决方式一、传递conn
  • spring,事务管理器用ThreadLocal存连接
  • 微服务,链路追踪
  • 动态数据源,用来保存当前线程应该使用哪个数据源

# 问题

  • 内存泄漏,ThreadLocalMap的key是弱引用,如果线程没有强引用指向ThreadLocal,那么就会回收,但是value是强引用,如果value没有回收,就会造成内存泄漏
编辑 (opens new window)
上次更新: 2024/06/01, 00:32:42
多线程-锁

← 多线程-锁

最近更新
01
杂乱无章
12-25
02
基础-大彬
11-14
03
集合-大彬
11-14
更多文章>
Theme by Vdoing | Copyright © 2023-2025 qouson
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式