Chiriri's blog Chiriri's blog
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)

Iekr

苦逼后端开发
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)
  • JavaSE

    • Java简介
    • 基础语法
    • 数据类型
    • 变量
    • 运算符
    • 输入输出
    • 流程控制
    • 循环语句
    • idea中的辅助键
    • 数组
    • 方法
    • 面向对象基础
    • 字符串
    • ArrayList集合
    • 继承
    • 修饰符
    • 多态
    • 抽象
    • 接口
    • 类名作为形参和返回值
    • 内部类
    • Api
    • 异常
    • 集合
    • 泛型
    • Set集合和比较器
    • 树
    • 哈希
    • 可变参数
    • 创建不可变的集合
    • Stream流
    • 方法引用
    • File
    • 多线程
    • 多线程高级
      • 线程状态
      • 线程池
        • 创建线程池
        • 提交 submit
        • 销毁 shutdown
      • ThreadPoolExecutor 自定义线程池
      • Volatile
        • 使用Synchronized同步代码块
      • 原子性
        • 原子类 Atomic
        • AtomicInteger
        • Synchronized 与 CAS 的区别(乐观锁和悲观锁)
      • Hashtable
      • ConcurrentHashMap
        • JDK1.7原理
        • JDK1.8原理
      • CountDownLatch
      • Semaphore
    • 网络编程
    • 类加载器
    • 反射
    • XML
    • 枚举
    • 注解
    • 单元测试
    • 日志
    • HTTP协议
    • Servlet
    • 请求对象
    • 响应对象
    • Cookie
    • Session
    • JSP
    • Listener
    • JDBC
  • JavaEE

  • Linux

  • MySQL

  • NoSQL

  • Python

  • Python模块

  • 机器学习

  • 设计模式

  • 传智健康

  • 畅购商城

  • 博客项目

  • JVM

  • JUC

  • Golang

  • Kubernetes

  • 硅谷课堂

  • C

  • 源码

  • 神领物流

  • RocketMQ

  • 短链平台

  • 后端
  • JavaSE
Iekr
2021-07-27
目录

多线程高级

# 多线程高级

# 线程状态

image-20210727153351109

# 线程池

# 创建线程池

创建一关默认的线程池,默认为空,(空参构造方法) 默认数量最多可以容纳 int 的最大值个线程

ExecutorService executorService = Executors.newCachedThreadPool(5);
1

带参构造为该线程池指定数量的线程池,并不是线程池创建就拥有指定数量的线程,而是该线程池可以拥有线程的上限

Executors 类为创建线程池对象类

ExecutorService 类为控制线程池类

# 提交 submit

把线程提交给线程池处理

# 销毁 shutdown

关闭线程池

executorService.shutdown();
1

# ThreadPoolExecutor 自定义线程池

ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
1

共有 7 个参数

  1. 核心线程数量 int 不能小于 0
  2. 最大线程数 int 不能小于等于 0. 并且大于等于核心数
  3. 空闲线程最大存活时间 int 不能小于 0
  4. 时间单位 Enum TimeUnit 中的常量
  5. 任务队列 传递一个阻塞队列 不能为 null 如果 submit 的线程过多则会缓存到队列中
  6. 创建线程工程 我们使用默认的线程池创建 Executors.defaultThreadFactory () 不能为 null
  7. 任务的拒绝策略 (即超出最大线程数如何处理) 我们使用 new ThreadPoolExecutor.AbortPolicy () 超出则拒绝 不能为 null 当 submit 线程数量超出了最大线程数 + 任务队列边界时 触发 拒绝策略
    • ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出 RejectedExecutionException 异常。默认的 cl
    • ThreadPoolExecutor.DiscardPolicy: 丢弃任务 但不抛出异常 不太推荐使用
    • ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中
    • ThreadPoolExecutor.CallerRunsPolicy: 调度任务的 run () 方法绕过线程池直接执行

# Volatile

  1. 堆内存是唯一的,每一个线程都有自己的线程栈
  2. 每个线程在使用堆里面变量的时候,都会先拷贝一个变量的副本中.
  3. 在线程中,每一次使用都是从变量副本中获取

如果 A 线程 修改了堆中共享的变量值,其他线程不一定能及时的使用最新的值

使用 Volatile 关键字可以解决这个问题,强制线程每次使用时,都会先去看一下共享区域中最新值

只需要共享数据前面加上 Volatile 关键字

Volatile int count = 100;
1

# 使用 Synchronized 同步代码块

使用 Synchronized 同步代码块,也可以解决此问题

  1. 线程获取锁
  2. 清空变量副本
  3. 拷贝共享变量中最新的值到副本中
  4. 执行代码
  5. 将修改后变量副本中的值赋值给共享数据
  6. 释放锁

# 原子性

原子性指的是在一次或多次操作中,要么所有的操作全部执行并且不会受到任何因素而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体

我们可以使用 Synchronized 锁 来强制协议统一共享数据的唯一性

而 Volatile 只是让线程在执行时检测在共享数据中检测最新的值并同步 副本中

# 原子类 Atomic

JDK1.5 提供了一个原子类 Atomic 该类下的的实现类都可以实现原子性,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的 CPU 架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。

# AtomicInteger

  • AtomicInteger (): 空参构造方法 默认为 0
  • AtomicInteger (int initialValue): 带参构造方法 指定值原子型
  • get (): 获取值
  • getAndIncrement (): 以原子方式加 + 1 并且返回自增前的值
  • incrementAndGet (): 以原子方式加 + 1 并且返回自增后的值
  • addAndGet (int data): 以原子方式 与指定值相加 并返回结果
  • getAndSet (int value): 以原子方式 设置原子型为指定值 并返回旧的值

原理:

自旋锁 + CAS 算法

CAS 算法:有 3 个操作数 (内存值 V , 旧的预期值 A , 要修改的值 B)

当旧的预期 A == 内存值 此时修改成功 将 V 改为 B

当旧的预期 A != 内存值 修改失败 不做任何操作

并重新获取现在的最新值 (这个动作称为自旋)

image-20210727174131889

# Synchronized 与 CAS 的区别 (乐观锁和悲观锁)

相同点:在多线程区块下,都可以保证共享数据的安全性

不同点:

  • Synchronized 总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每次操作共享数据之前,都会上锁.(悲观锁)
  • CAS 是乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会去检查一下,别人有没有修改过这个数据. (乐观锁) 如果别人修改过,那么再次获取最新的值 如果别人没有修改过,那么直接修改共享数据的值

# Hashtable

在多线程下使用 HashMap 不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题。

  1. Hashtable 采用悲观锁 Synchronized 的形式保证数据的安全性
  2. 只有有线程访问,会将整张表全部锁起来,所以 Hashtable 效率低下
  3. 底层原理与 hashmap 一样也是数组 + 链表实现,其他无异

# ConcurrentHashMap

如果 map 集合要使用多线程我们可以使用 ConcurrentHashMap, 它线程安全,效率较高

Hashtable 已经被淘汰了

# JDK1.7 原理

  1. 创建一个默认长度为 16, 默认加载因 位 0.75 的数组 数组名为 Segmewnt 无法扩容
  2. 再创建一个长度为 2 的小数组 (数组名为 HashEntey) 把地址值赋值给 Segmewnt 数组中的索引 0 (模板) 其他索引都为 null
  3. 根据键的哈希值计算出 Segmewnt 数组索引
  4. 如果此索引为空 就会创建一个长度默认为 2 的数组 并把这个小数组的地址值 赋值给 该索引
  5. 再次利用键的哈希值计算出在 小数组 应存入的索引 (二次哈希)
  6. 如果为空,则直接添加 如非空则 equals 比较 不同则存入 (链表形式挂载在新元素下面)
  7. 小数组的加载因 同样为 0.75 当超过了 2*0.75=1.5 强转为 int=1 会自动扩容 2 倍
  8. Segmewnt 数组无法扩容 (恒定为 16) 因为只有 HashEntey 小数组在扩容
  9. ConcurrentHashMap 通过 Segmewnt 索引 来加锁 (悲观锁 Synchronized) 所以在 JDK1.7 默认情况下,最多允许 16 个线程同时访问

# JDK1.8 原理

底层结构:哈希表 (数组 链表 红黑树结合体)

结合 CAS 机制 + Synchronized 同步代码块形式来保证线程安全

  1. 如果使用空参构造方法创建 ConcurrentHashMap, 则什么事情都不做。只有在第一次添加元素时候创建哈希表
  2. 计算出当前元素应存入的索引
  3. 如果该索引为为 null, 则利用 CAS 算法,将本结点添加到数组中
  4. 如果该索引不为 null, 则利用 Volatile 关键字获取当前位置最新的结点地址,挂载到它的下面,变成链表
  5. 当链表长度大于等于 8 时,自动转成红黑树
  6. 以链表或红黑树头结点为锁对象,配合悲观锁 (Synchronized) 保证多线程数据的安全性

# CountDownLatch

让某一条线程等待其他线程执行完毕之后在执行.

  • CountDownLatch (int count): 带参构造方法 传递线程数,表示等待线程数 定义一个计数器
  • await (): 让线程等待 等待其他线程执行完毕后才执行 计数器为 0 执行
  • countDown (): 当前线程执行完毕 将计数器 - 1

# Semaphore

可以控制访问特定资源的线程数量 在线程类中创建并使用

  • Semaphore (int permits): 带参构造方法 最多允许多少条线程同时执行
  • acquire (): 获取通信证
  • release (): 归还通信证
编辑 (opens new window)
上次更新: 2023/12/06, 01:31:48
多线程
网络编程

← 多线程 网络编程→

最近更新
01
k8s
06-06
02
进程与线程
03-04
03
计算机操作系统概述
02-26
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Iekr | Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式