当前位置: 首页 > news >正文

线程(Thread)

目录

线程(Thread)

线程的创建方式 实现方式

Runnable和Callable的区别

线程的命名和优先级

线程的六种状态

线程的插队

线程的中断

线程的让出

守护线程

设置线程为守护线程

sleep()和wait()的区别

线程的同步synchronized锁

语法格式

实现原理

锁升级(锁优化、所膨胀)

线程安全的案例

ReentrantLock锁

并发集合

CopyOnWriteArrayList

思想

并发修改时保证线程安全

并发读取

CopyOnWriteArraySet

BlockingQueue阻塞队列

ArrayBlockingQueue:有界队列

LinkedBlockingQueue:无界队列

ConcurrentHashMap

线程池

常用方法

执行流程

配置参数

拒绝策略

常用线程池


线程(Thread)

线程的创建方式 实现方式

创建方式只有一种:通过Thread创建

实现方式有四种分别是:

  1. 继承Thread类实现

  2. 传入Runnable接口实现类实现

  3. 传入Callable接口实现类实现(要用FutureTask转化为runnable)

  4. 通过线程池实现

Runnable和Callable的区别

  1. runnable接口中的run()方法没有返回值,callable接口中的call()方法有返回值

  2. callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,runnable接口实现类中run方法的异常必须在内部处理,不能向上抛出。

线程的命名和优先级

命名

  1. 在实例化Thread时传入线程名

  2. setName()方法设置线程名

优先级:setPriority()方法设置,(最大10,最小1,默认5)

线程的六种状态

线程创建后为new状态,start()启动后进入runnable可运行状态,run()方法执行结束后进入terminated终止状态,runnable有三个分支状态,当多个线程竞争时,没有获取到锁的线程进入blocked阻塞状态。调用Object.wait()方法和Thread.join()方法后会进入waiting等待状态,调用notify()方法唤醒线程重新进入runnable状态。调用Thread.join(时间值)、Object.wait(时间值)、Thread.sleep(时间值)等方法进入timed_waiting计时,等待状态时间结束后恢复runnable可运行状态。

线程的插队

调用 join() 方法实现插队。

底层通过调用Object的wait方法实现,因为线程在die的时候会自动调用自身的notifyAll方法,来释放所有的资源和锁。

线程的中断

interrupt()

修改中断状态为true,抛出InterruptedException,执行结束。

线程的让出

yield() 让出一次

守护线程

设置线程为守护线程

通过 setDaemon(true) 方法设置

守护线程是用来为用户线程服务的,当一个程序中的所有用户线程都结束之后,无论守护线程是否在工作都会跟随用户线程一起结束。

sleep()和wait()的区别

  1. 休眠过程中,当前线程不会让出持有的"锁",等待过程中,当前线程会让出持有的"锁"

线程的同步synchronized锁

线程安全问题:多个线程同时访问竞争一个资源时,会导致数据损坏不一致。

保证一段代码的原子性就是通过加锁和解锁实现的,实现线程的同步安全,参数对象一致则使用同一个锁。

            synchronized (mutex) {Counter.count-=1;}

synchronized是一个关键字

语法格式

代码块:自定义对象作为锁

普通方法前:this,代表调用该方法的对象

静态方法前:该静态类Class对象

实现原理

1.通过Monitorenter和Monitorexit两个指令实现

2.通过monitor监视器机制实现线程同步

WaitSet:线程等待区

EntryList:线程阻塞区

Owner:线程拥有者

锁升级(锁优化、所膨胀)

在JDK1.6之前,synchronized性能开销较大;在JDK1.6之后,对synchronized进行了优化,它会自动根据程序的执行情况,自动进行锁的升级:偏向锁->轻量级锁->重量级锁

偏向锁(偏斜锁):只有一个线程访问时,使用偏向锁,通过Owner记录线程ID实现

轻量级锁:出现多个线程访问时(没有并发),使用轻量级锁,通过CAS实现

重量级锁:出现多个线程并发访问时,使用重量级锁。由于重量级锁,使用操作系统的互斥锁实现。(使用互斥锁,从“用户态”切换至“内核态”,带来性能开销,所以性能相对较差)

线程安全的案例

可变字符串的线程安全: StringBuffer : 线程安全(在改变字符串内容的方法上使用synchronized同步锁),性能较差

StringBuilder:线程不安全,性能较好

集合类的线程安全(使用synchronized关键字实现线程安全)

List接口的线程安全实现类:Vector,Stack

Map接口的线程安全实现类:Hashtable

ReentrantLock锁

1.ReentrantLock是核心类库提供的锁实现类,实现了Lock接口,通过lock()方法加锁,unlock()方法释放锁。

2.通过trylock()方法支持获取锁的尝试机制

3.支持公平锁和非公平锁,内部通过AQS机制实现

ReentrantLock实现线程安全的案例

CopyOnWriteArrayList

ArrayBlockingQueue

并发集合

List Set Map Queue

CopyOnWriteArrayList

思想

修改时将原数组内容复制Copy到新数组内,在新数组内修改,然后替换

并发修改时保证线程安全

通过ReentrantLock实现多个线程并发修改时的线程安全同步(添加元素的同时,不允许删除)

添加新元素:list.add("")

按照指定下标替换元素:list.set(index, element)

按照指定下标删除元素:list.remove(0)

并发读取

没有加锁,允许多个线程同时并发读取;但是读取时,可能产生脏读(读取的同时,允许写入操作)。

CopyOnWriteArraySet

内部通过一个CopyOnWriteArrayList实现

BlockingQueue阻塞队列

阻塞队列:有两个线程,分别进行读写(task和put)操作;读取时,不允许写入,如果队列为空,则读取线程阻塞;写入时,不允许读取,如果队列已满,则写入线程阻塞;

经常用于生产消费场景

ArrayBlockingQueue:有界队列

LinkedBlockingQueue:无界队列

ConcurrentHashMap

JDK1,7:通过分段锁实现线程安全

JDK1,8:通过 synchronized+CAS实现线程安全

当产生哈希冲突时,通过synchronized将根节点作为锁,进行线程的同步安全

在没有产生哈希冲突时,通过CAS进行无锁化操作,降低synchronized进行线程同步操作所引发的性能下降

线程池

常用类和接口

ExecutorService接口:线程池的操作

Executors类:工具类,提供了常见线程池的封装

ThreadPoolExecutor类:具体线程池实现类

常用方法

void execute(Runnable command):提交线程任务

Future<T> submit(Callable<T> task):提交线程任务,可以获取线程执行结果

void shutdown():将线程池中的线程任务执行完毕后,关闭线程池

List<Runnable> shutdownNow():立刻关闭线程池,并返回未完成的线程任务

执行流程

  1. 提交线程任务,分配空闲线程

  2. 判断“工作线程数”是否超出“核心线程数”,如果未超出,则创建新线程;

  3. 如果超出,将线程任务存入工作队列

  4. 如果工作队列已满,判断判断“工作线程数”是否超出“最大线程数”,如果未超出,则创建新线程

  5. 如果超出,则执行拒绝策略

配置参数

核心线程数

最大线程数

线程存活时间

工作队列

线程工厂

拒绝策略

AbortPolicy(默认拒绝策略):丢弃当前线程任务,并抛出RejectedExecutionException

AbortPolicy:丢弃当前线程任务

DiscardOldestPolicy:丢弃工作队列中最早入队的线程任务

CallerRunsPolicy:由当前调用线程处理执行线程任务

常用线程池

Executors.newFixedThreadPool(3):固定数目的线程池

Executors.newCachedThreadPool():动态数目的线程池(线程被缓存,提供重复使用效率)

Executors.newSingleThreadExecutor():仅包含1个线程的线程池(将大量的线程任务保存至工作队列,然后按照提交顺序,用一条线程依次处理)

Executors.newScheduledThreadPool(5):可以按照时间进行调度执行任务的线程池

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 从“N 号房”看Deepfake乱象,如何证明“我”不是我?
  • C++之打造my vector篇
  • 【H2O2|全栈】关于HTML(1)认识HTML
  • Java通过jna调用c++动态库
  • 基于 AT 固件测试 ESP32 设备作为 WiFi AP 模式创建 TCP Server 开启 UART-to-WiFi 透传模式的指令序列
  • TCP通信实现
  • C++里定义和声明的区别
  • Java数组的定义及遍历
  • 常见分组加密算法的整体结构
  • 第六章 SqlSession 执行 Mapper 过程
  • 学习Power BI第一步先从安装开始(一)
  • springboot系列--自动配置原理
  • QT 联合opencv 易错点
  • 自动驾驶相关的理论基础
  • C语言-数据结构 无向图迪杰斯特拉算法(Dijkstra)邻接矩阵存储
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • Docker下部署自己的LNMP工作环境
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Java|序列化异常StreamCorruptedException的解决方法
  • java第三方包学习之lombok
  • JS变量作用域
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • Mysql优化
  • Node 版本管理
  • Python语法速览与机器学习开发环境搭建
  • 彻底搞懂浏览器Event-loop
  • 飞驰在Mesos的涡轮引擎上
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 面试总结JavaScript篇
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 如何利用MongoDB打造TOP榜小程序
  • 入门到放弃node系列之Hello Word篇
  • 深度解析利用ES6进行Promise封装总结
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 微信小程序--------语音识别(前端自己也能玩)
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • ![CDATA[ ]] 是什么东东
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #vue3 实现前端下载excel文件模板功能
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (02)Hive SQL编译成MapReduce任务的过程
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (多级缓存)多级缓存
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (转)setTimeout 和 setInterval 的区别
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .Net Core 中间件与过滤器
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .net/c# memcached 获取所有缓存键(keys)
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args