首页 > 其他分享 >多线程7

多线程7

时间:2024-09-05 12:35:43浏览次数:9  
标签:调用 Thread ThreadLocalMap ThreadLocal 线程 多线程 方法

  1. 多线程知识点概述
    1.什么是多线程
    1)一个进程中往往有多条程序执行路径,该程序可以理解为多线程程序
    2)java程序的入口是main方法,从操作系统的角度看,main方法的启动意味着启动了一个java进程。main方法所在的程序执行路径即为主线程,同时,main线程的启动,会伴随着其他线程的创建和启动,如垃圾回收线程等
    3)线程的创建者:主线程的创建者是jvm,普通线程可以通过调用JDK的相关API进行创建

2.多线程的作用
1)更大的吞吐量
2)提升性能

3.多线程的基本使用(代码:test219_thread/test/demo1)
1)线程任务的定义
2)线程的创建和启动

4.多线程带来的问题(代码:test219_thread/test/demo2)
1)线程安全问题(本质上的体现是线程之间的共享数据出现不正确的结果)
2)活跃性问题(死锁、饥饿、活锁以及丢失信号)
3)性能问题(线程的创建和销毁、线程的上下文切换)

5.线程安全描述的对象(代码:test219_thread/test/demo3)
1)类线程安全
2)程序线程安全

6.JDK提供几种方式来保证线程安全,分别是:synchronized、volatile、final、CAS、ThreadLocal

7.线程的状态,官方定义的方式有六种,定义在枚举java.lang.Thread.State中
1)新建状态: Thread.State.NEW (Thread t = new Thread();)
2)可运行状态: Thread.State.RUNNABLE (包括两种:在等待CPU分配时间片、运行状态)
3)阻塞状态: Thread.State.BLOCKED (线程在等待同步锁,试图进入同步代码块、一种是IO阻塞)
4)等待状态: Thread.State.WAITING
等待状态意味着线程放弃CPU的执行资格,退出CPU的线程处理队列,即向CPU发出一个信号,不需要分配时间片给这个线程。同时释放锁。需要其他线程调用notify方法唤醒,然后转变为可运行状态,继续进入CPU的线程处理队列
5)计时等待状态:Thread.State.TIMED_WAITING
计时等待状态意味着线程放弃CPU的执行资格,退出CPU的线程处理队列,即向CPU发出一个信号,不需要分配时间片给这个线程。不会释放锁。直到设置的睡眠时间结束之后,然后转变为可运行状态,继续进入CPU的线程处理队列
6)终止状态: Thread.State.TERMINATED

8.wait/notify机制(代码:test219_thread/test/demo4)
1)涉及到的方法
· wait():让线程进入等待状态,退出CPU的线程处理队列,释放锁,线程被存储到对象obj的线程等待集中
· notify():唤醒对象obj的线程等待集中的任意一个线程
· notifyAll():唤醒对象obj的线程等待集中的所有线程

2)注意点
· 这几个方法必须定义在同步块或者同步方法中,因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程
· 这几个方法定义在Object中的原因:因为这几个方法是对象监视器的方法,对象监视器其实就是同步锁,同步锁可以是任意对象,所以任意对象都可以调用的方法只能定义在Object中

9.Java内存模型:假如有一个线程的共享变量,这个变量是存在于主内存中的,但是为了提高效率,每个线程在自己的工作内存中有一份该变量的拷贝。(主内存映射到硬件可以理解为平常说的内存,而工作内存可以理解为CPU中的高速缓冲存储器,CPU从高速缓冲存储器中读数据肯定要比从内存中读数据要快得多)

10.中断机制(代码:test219_thread/test/demo5)
1)在Java中,停止一个线程的主要机制是中断,中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何以及何时退出

2)通过Thread类的interrupt()方法来向线程发出中断请求

3)线程在不同状态下对于中断请求所产生的反应
· 如果一个线程处于新建状态或者终止状态,对于中断请求几乎是屏蔽的
· 如果一个线程处于RUNNABLE状态或者阻塞状态,对于中断请求只是设置中断标志位并没有强制终止线程
· 如果一个线程处于睡眠状态或等待状态,对于中断请求是敏感的,线程会从睡眠状态或等待状态强制恢复到可运行状态,重新进入到CPU的线程处理队列,同时线程会抛出中断异常并清空中断标志位

4)关于设置中断标志位和清空中断标志位
· 设置中断标志位:指的是把中断状态设置为true,即Thread.currentThread().isInterrupted()返回true
· 清空中断标志位:指的是把中断状态设置为false,即Thread.currentThread().isInterrupted()返回false

5)中断机制的应用
· 对于处于RUNNABLE状态或者阻塞状态的线程,可以通过判断中断状态来决定线程是否要终止
· 对于处于睡眠状态或等待状态的线程,可以通过捕获中断异常之后来决定线程是否要终止

6)与线程中断有关的三个方法:interrupt()、interrupted()、islnterrupted()。(均在Thread类中)

11.juc包
1)AQS
2)Lock
3)并发工具类
4)原子类

  1. 解决线程安全问题的方式
    一、synchronized(代码:test219_thread/test/demo6)
    1)synchronized的基本使用
    · 修饰语句块
    · 修饰方法

2)synchronized的实现原理
· 修饰语句块
· 修饰方法

3)synchronized锁的升级

4)synchronized实现可重入的原理
· 每一个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁

5)synchronized与wait/notify机制

6)验证同步方法中的锁对象
· 非静态方法的锁对象为this
· 静态方法的锁对象为Class对象

二、volatile(代码:test219_thread/test/demo7)

三、final

四、CAS
· 主要是通过sun.misc.Unsafe来实现CAS

五、ThreadLocal
1.ThreadLocal 是什么

1)ThreadLocal 直译为线程本地,也称为线程本地变量

2)意思是在 ThreadLocal 变量中填充的值只属于当前线程,对其它线程是不可见的,从而起到线程隔离的作用,避免了线程安全问题

2.ThreadLocal 的作用(为什么需要 ThreadLocal)

1)在线程之间隔离数据,对于同一个变量,每个线程都有独立的数据
2)减少锁的使用,如果没有 ThreadLocal,在某些并发场景下需要加锁来解决

3.ThreadLocal 的常用方法
· ThreadLocal() 创建 ThreadLocal 对象
· set(T value) 设置当前线程绑定的局部变量
· T get() 获取当前线程绑定的局部变量
· remove() 移除当前线程绑定的局部变量

4.ThreadLocal 的基本使用(代码:test219_thread/test/demo8)

5.ThreadLocal 常见的应用场景

1)web 请求,在 Controller 解析出用户信息后放入 ThreadLocal 中,在 Service 层或者其它位置可以直接通过 ThreadLocal 获取到用户信息
2)JDBC,一个线程中需要多次操作数据库,并且需要保证线程内的事务,可以在操作数据库之前把数据库连接放入 ThreadLocal 中,后续在本线程的任意方法都可以通过 ThreadLocal 获取到数据库连接
(共同点都是需要在一个线程内的不同方法获取某一类型(用户信息、数据库连接)的数据,通过使用 ThreadLoca 就可以减少参数在方法之间的传递)

6.ThreadLocal 的实现原理

1)ThreadLocal、ThreadLocalMap、Thread 三者关系

· ThreadLocalMap 是 ThreadLocal 的内部类,ThreadLocalMap 通过 Entry[] 来存储数据,Entry 是 ThreadLocalMap 的内部类,key 为 ThreadLocal 对象(实际上 key 为指向 ThreadLocal 实例的弱引用),value 为 set 进 ThreadLocal 的值

· Thread 持有 ThreadLocalMap,变量名称为 threadLocals

2)数据流转过程

· 调用 set() 方法时,先获取当前线程的 ThreadLocalMap 对象,如果 ThreadLocalMap 为空则创建一个,然后把 ThreadLocal 对象和 set 进 ThreadLocal 的值放入 ThreadLocalMap 的 Entry[] 中

· 调用 get() 方法时,先获取当前线程的 ThreadLocalMap 对象,然后从 ThreadLocalMap 中根据 ThreadLocal 对象(应该是计算哈希值再取模)找到对应的 Entry,获取 Entry 的 value 返回

· 调用 remove() 方法时,先获取当前线程的 ThreadLocalMap 对象,然后从 ThreadLocalMap 中根据 ThreadLocal 对象找到对应的Entry 删除

7.ThreadLocal 内存泄露问题
1)ThreadLocal 内存泄露是指 ThreadLocalMap 中的 value 没办法被回收

2)内存泄露原因:
ThreadLocal 已经使用结束了(意味着没有强引用指向堆中的 ThreadLocal 对象),而线程还存活着,JVM 在进行垃圾回收后会把只有弱引用指向的 ThreadLocal 对象回收,也就是 Entry 的 key 会被回收,但是此时 value 还在,因此就产生了内存泄露

3)如何避免内
内存泄露:在使用完 ThreadLocal 之后,调用 remove() 方法把 Entry 置空

8.ThreadLocal 的最佳实践

· 避免过度使用 ThreadLocal
· 使用完 ThreadLocal 要调用 remove 方法进行清除,以避免内存泄露

9.ThreadLocal 有什么缺点,如何改进

10.零碎问题
1)ThreadLocal 对象在业务代码中被创建,该对象在两个地方被引用,被业务代码强引用、被 ThreadLocalMap 的 key 弱引用
2)弱引用的特点
:只要进行垃圾回收,不管 jvm 内存是否充足,都会回收只被弱引用的对象(如果对象同时有强弱引用时,则不回收)
3)key 设计成弱引用是为了垃圾回收时可以保证 key 指向的 ThreadLocal 对象被回收,而调用 remove() 方法是保证 Entry 中的 value 可以尽快被回收

4)在调用 get()、set() 方法的某些时候,会伴随调用 expungeStaleEntry() 方法把 key 为 null 的 Entry 置空,以此来尽可能避免内存泄露
5)如果线程执行完正常销毁,是不会存在内存泄露的,因为 Thread 对象被回收后,ThreadLocalMap 自然也会被回收

6)ThreadLocal 尤其是当与线程池结合使用时,由于线程需要被复用而无法销毁,因此使用完 ThreadLocal 如果没有及时清理,就会导致内存泄露
7)remove() 方法要放在 finally 中

8)ThreadLocalMap 使用开放寻址法解决哈希冲突
9)如果需要在父子线程之间传递 ThreadLocal 填充的数据,可以使用 InheritableThreadLocal
10)ThreadLocal 不能平替 synchronized,ThreadLocal 用于实现线程隔离,而 synchronized 用于实现线程同步

  1. juc包
    一、AQS

二、Lock

三、并发工具类(代码:test219_thread/test/demo9)
1.基本概述
1)CountDownLatch、CyclicBarrier、Semaphore 可以认为是一种控制并发流程的工具类
2)Exchanger 可以认为是线程间交换数据的工具类

2.CountDownLatch
1)CountDownLatch 的核心思想是通过计数器来控制线程的执行顺序,当计数器的值降为0时,所有等待的线程都会被唤醒,然后开始执行下一步操作

2)CountDownLatch 的使用:test219_thread/demo9/CountDownLatchDemo

3)CountDownLatch 的执行过程
· 在主调用线程中创建 CountDownLatch,并传入 count,count 通常为工作线程数量
· 在工作线程中调用 countDown() 方法,每调用一次 count 就减1

· 在主调用线程中调用 await() 方法来阻塞主调用线程

· count减到为0时,主调用线程被唤醒

4)CountDownLatch 的底层实现:基于 AQS 实现

3.CyclicBarrier
1)CyclicBarrer 的作用是让一组线程达到一个屏障(同步点)时被阻塞,直到所有的线程到达此屏障时,才会唤醒被屏障阻塞的所有线程

2)CyclicBarrer 的使用:test219_thread/demo9/CyclicBarrierDemo、CyclicBarrierDemo2、CyclicBarrierDemo3、CyclicBarrierDemo4

3)CyclicBarrer 的执行过程
· 在主调用线程中创建 CyclicBarrer,并传入 parties,parties 通常为工作线程数量
· 在工作线程中调用 await() 方法,每调用一次,就说明有一个线程抵达屏障,直到有 parties 个线程抵达屏障后,唤醒被屏障阻塞的所有线程

4)CyclicBarrier 的底层实现:基于 AQS 实现

4.Semaphore

1)信号量是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源

2)Semaphore 的使用:test219_thread/demo9/SemaphoreDemo

3)Semaphore 的工作流程
· 在主调用线程中创建 Semaphore,并传入 permits,permits 为许可证数量
· 在工作线程中调用 acquire() 方法,每调用一次,许可证数量就减1
,当许可证数量减到为0时,再调用 acquire() 会被阻塞,直到已经获得许可证的工作线程调用 release() 方法归还许可证,然后被阻塞的线程会获得许可证

4)Semaphore 的底层实现:基于 AQS 实现

5.Exchanger
Exchanger 的底层实现:使用 ThreadLocal 和 ArrayBlockingQueue 等数据结构来实现线程间的配对和数据交换

6.零碎问题
1)CountDownLatch 和 CyclicBarrier 的区别
· CountDownLatch 阻塞的是主线程,下一步动作的执行者是主线程,不可重复使用
· CyclicBarrier 阻塞的是其它线程,下一步动作的执行者是其它线程,可重复使用

四、原子类(代码:test219_thread/test/demo10)
1)原子类的出现背景:当一个线程更新一个变量时,程序如果没有正确的同步,那么这个变量对于其他线程来说是不可见的。我们通常使用 synchronized 或者 volatile 来保证线程安全的更新共享变量。在JDK1.5中,提供了 java.util.concurrent.atomic 包,这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式

2)原子类的应用案例:多个线程进行 i++ 操作,为了线程安全,需要加 synchronized 锁,锁是比较重的,因此可以考虑使用原子类AtomicInteger 来代替 synchronized

3)原子类的底层实现是基于 volatile 和 CAS,因此可以减少锁带来的性能开销

  1. 多线程在实际业务场景中的使用
    1.基本使用
    1)主调用线程需要等待各个线程的返回结果(代码:test/demo11/ThreadInActualBusiness.needThreadResult())
    2)主调用线程不需要等待各个线程的返回结果(代码:test/demo11/ThreadInActualBusiness.main())
    (这种场景用得相对较少,因为会出现:接口已经完成响应,但是业务处理还没有结束的情况)

线程池
4.线程池
1)线程池的顶层接口和类
· Executor:Executor 是一个接口,定义了一个基本的执行方法 execute(Runnable command),用于执行给定的 Runnable 任务。它是所有线程池执行机制的基础
· ExecutorService:ExecutorService 也是接口,它扩展了 Executor 接口,提供了更丰富的线程池控制方法,如 submit(),以及线程池的关闭方法 shutdown() ,ExecutorService 是创建和管理线程池的主要接口
· AbstractExecutorService:实现了 ExecutorService 接口的部分方法,为创建具体的线程池服务提供了基础框架。它提供了线程池的一些通用实现,如关闭线程池的行为
· ThreadPoolExecutor:ThreadPoolExecutor 是 AbstractExecutorService 的一个具体实现,提供了完整的线程池实现。它允许开发者精细控制线程池的参数,如核心线程数、最大线程数、工作队列类型、线程工厂、拒绝策略等。ThreadPoolExecutor 是 Java 中线程池的核心实现类

2)线程池的使用方式
· 直接创建 ThreadPoolExecutor
· 通过 Executors 工具类创建线程池(本质上也是创建 ThreadPoolExecutor)
static ExecutorService newFixedThreadPool(int nThreads)
static ExecutorService newCachedThreadPool()
static ExecutorService newSingleThreadExecutor()
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

实现多线程的方式
1)继承 Thread 类
2)实现 Runnable 接口
3)实现 Callable 接口 + FutureTask
4)线程池(ExectutorService 结合 Callable、Future 实现有返回结果的多线程)

标签:调用,Thread,ThreadLocalMap,ThreadLocal,线程,多线程,方法
From: https://www.cnblogs.com/chaoshang8/p/18398170

相关文章

  • 多线程5
    102.final如果修饰的是集合,则没有办法保证线程安全103.通过Executors返回的线程池对象存在的弊端:·FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM·CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为......
  • 【python】socket 入门以及多线程tcp链接
    Socket入门及多线程tcp链接网络基础知识三要素Socket是套接字的意思,是网络编程的核心对象,通信两端都独有自己的Socket对象,数据在两个Socket之间通过字节流(TCP协议)或者数据报包(UDP协议)的形式进行传输.本文主要针对tcp流程进行讲解socket-tcp流程图1.创建......
  • SQLServer事务复制延迟优化之多并行(多线程)复制
    事务复制的延迟在数据库的主从复制过程中,包括MySQL的主从复制,SQLServer的事务复制等等,鉴于主节点往往是并发写入的,而从节点(SQLServer中叫做订阅节点)在重放主节点的写操作的时候,往往会产生一定时间的延迟,如何降低这种复制延迟,并行复制或者说多线程复制是其中手段之一。 SQLServ......
  • 【Linux修行路】多线程——互斥量
    目录⛳️推荐一、多线程模拟抢票二、加锁——互斥量2.1pthread_mutex_init——初始化互斥量2.2pthread_mutext_destroy——销毁一个互斥量2.3pthread_mutex_lock——加锁2.4pthread_mutex_trylock——非阻塞的申请锁2.4pthread_mutex_unlock——解锁2.5定义一个......
  • Linux C++ 多线程高并发服务器实战项目一
    1、项目介绍1、按照包头+包体的格式收发数据包,解决粘包的问题2、非常完整的多线程高并发服务器3、根据收到数据包执行,不同的业务逻辑函数用到的技术:epoll高并发通讯技术,用的是水平触发【LT】水平触发模式通过线程池技术处理业务逻辑多线程、之间同步技术使用,互斥量、和条件变......
  • 二、并发编程与多线程-2.1、J.U.C和锁(下篇)
    2.1、J.U.C和锁(下篇)2.1.8、什么是可重入锁?它的作用是什么?答:在Java中,可重入锁是一种同步机制,它允许同一个线程多次获取同一个锁。当一个线程持有该锁时,它可以反复进入被该锁保护的代码块,而不会被阻塞。这种机制也被称为递归锁。比如synchronized锁和ReentrantLock锁都是可......
  • 多线程、任务、异步的区别
    Task和Thread的区别这是一个高频,深刻的问题,无论去哪都逃不过被询问这个问题。Task是基于Thread的,这是众所周知的。但是Task和Thread的联系如此简单和纯粹确实我没想到的。甚至只需要几十行代码就能呈现其原理。一个简单的模拟实例说明Task及其调度问题,这真是一篇好文章。任务体......
  • 【多线程】 - 实现方法以及自定义线程池
    概念进程进程是程序的基本执行实体线程线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。多个线程组成了多线程多线程应用场景软件中的耗时操作拷贝、迁移大文件加载大量的资源文件想让多个事情同时运行就需要多线程并发和并行并发在同一时刻......
  • 二、并发编程与多线程-2.1、J.U.C和锁(中篇)
    2.1、J.U.C和锁(中篇)2.1.4、什么是CAS?答:CAS是Java中Unsafe类里面的方法,全称是CompareAndSwap,是比较并交换的意思。作用就是保证在多线程环境下,对于修改共享变量操作的原子性。扩展:CAS保证修改共享变量操作原子性的实现逻辑:CAS方法里有三个参数,依次分别是共享变量的内......
  • flask多线程下数据库操作(简单示例)
    前言背景:开了两个线程操作数据库插入但是获取不到db的信息,自己摸索的方法不一定是最佳的,有更好的可以评论或私信,感谢大佬话不多说,直接上代码 #模型里面的多线程新增操作@staticmethoddefadd_users_by_thread(username,password,session):user=U......