Java 多线程/并发
一、概念组
1. 进程与线程
A. 进程:是指处于运动过程中的程序,并且具有一定的独立功能,它是系统进行资源分配和调度的一个单位,如QQ.exe,程序;
B. 线程:是进程的一个执行单元,一个进程中至少包含一个线程,线程是CPU调度的,如Java程序的main、GC线程;
C. 守护线程:是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件,如Java中垃圾回收线程;
2. 并发与并行
A. 并发(Concurrency):是多个事件在同一时间间隔发生,多线程操作一个资源类,真实情况是快速交替过程;
B. 并行(Parallelism):是多个事件在同一时刻发生,多核多CPU才有;
3. 同步与异步
A. 同步(Synchronous):即调用方法开始,一旦调用就必须等待方法执行完返回才能继续下面的操作,针对一个线程执行;
B. 异步(Asynchronous):即不关心方法执行的过程,触发要调用的方法就继续执行下面的操作;
4. 阻塞与非阻塞
A. 阻塞(Blocking):即如果一个线程占用了一个公共资源而没有释放对它的锁,另外别的一些线程想要继续执行就只能等待它释放锁,针对多个线程执行;
B. 非阻塞(Non-Blocking):就是没有阻塞,线程可以自由运行,没有锁定公共资源;
二、线程状态
1. 线程六大状态(Thread.State)
public enum State { NEW, RUNNABLE, BLOCKED, WAITTING, TIMED_WAITTING, TERMINATED; }
A. 新建状态(New):创建后尚未启动,即new了一个线程;
B. 可运行状态(Runnable):可能正在运行,也可能正在等待CPU时间片,即调用了线程start(),所有的线程都在等待队列里,等待CPU调度;
C. 阻塞状态(Blocked):等待获取一个排它锁,如果线程释放了锁就会结束此状态;
D. 等待(Waitting):等待其它线程显示地唤醒,否则不会分配CPU时间片,如调用了Object.wait()/Thread.join()/LockSupport.park()之后,进入等待状态;
E. 超时等待(Timed Waitting):无需等待其它线程唤醒,在一定时间之后,会被系统自动唤醒;
F. 死亡状态(Terminated):可以是线程任务完成之后正常结束,或者抛出了未捕获的异常导致异常结束。
2. 线程使用方式
A. 实现Runnable接口:实现Run方法,优先使用(因为继承类开销过大且类只支持单继承),特点是无返回值,不能抛出异常;
package java.lang; public interface Runnable { public abstract void run(); }
B. 实现Callable接口(JUC包):实现Call方法,特点是有返回值,可以向上抛出异常,使用需要通过FutureTask封装,调用futureTask.get()方法阻塞主线程获取结果;
package java.util.concurrent; public interface Callable<V> { V call() throws Exception; }
C. 继承Thread类:重写Run方法。
注意:实现Runnable和Callable接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过Thread来调用。
3. 线程之间的协作
A. join():在线程中调用另一个线程的join()方法,会将当前线程挂起,而不是忙等待,直到目标线程结束;
B. wait()与notify()/notifyAll():只能用在同步方法或同步代码块中
wait:使线程等待,线程等待时会被挂起,挂起期间,线程释放锁;
notify:唤醒wait挂起的其中一个线程;
notifyAll:唤醒wait挂起的所有线程,线程将参与锁的竞争,若没竞争到,就会参与下次锁的竞争;
C. await()与signal()/signalAll():JUC包下的,类似wait,但是比它灵活,因为可以指定等待的条件。
4. 线程重要的API
A. yield():表示当前线程主动让出CPU资源一下,然后再一起去抢,是从执行中回到等待CPU分配资源;
B. sleep():使当前线程休眠,它会抛出一个InterruptedException异常;
C. setPriority():设置线程的优先级,虽然优先级高的线程并不能百分百保证一定会先执行,但它是有更大的概率被先执行的;
D. currentThread():获取当前线程对象;
E. interrupt():中断当前线程,实际上是设置一个中断标志,线程仍会继续运行;
F. interrupted():测试当前线程是否被中断,返回boolean值并清除中断标志。
5. 线程上下文切换
A. 定义:当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据。
三、线程安全
1. 分类
A. 不可变:如使用final修饰的基本数据类型;
B. 绝对线程安全
C. 相对线程安全:使用额外的同步手段(如锁)来保证调用的正确性;
D. 线程兼容:使用栈封闭来保证线程安全;
E. 线程对立。
2. 实现方法
A. 互斥同步:Synchronized和ReentrantLock锁;
B. 非阻塞同步:CAS和JUC包下原子类;
C. 无同步方案:栈封闭和线程本地存储(ThreadLocal)。
四、ThreadLocal
1. 定义:ThreadLocal是一个将在多线程中为每一个线程创建单独的变量副本的类;
2. 作用:实现线程隔离,解决因多线程操作共享变量而导致的数据不一致情况;
3. 应用场景
A. 数据库连接管理;
B. 用户session管理;
4. 缺点:可能会因为线程的执行周期过长造成内存泄露问题,解决办法是使用remove方法加快内存的释放。
5. 重要方法
A. initialValue():ThreadLocal初始化的值,新建ThreadLocal的一般要重写给个初始的值;
B. get():获取当前线程变量的副本值;
C. set(T value):设置、更新当前线程上的变量副本值;
D. remove():移除当前线程上的变量副本;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗