目录
1.基本概念
2.创建线程方式
2.1直接建立线程
2.2实现Runnable接口
3.3实现Callable接口
3.4 了解Future接口
Future模式主要角色及其作用
3.5实例化FutureTask类
3.实现线程安全
3.1定义
3.2不安全原因
3.3解决方案
3.4volatile与synchronized区别
4.极端情况——线程死锁
4.1定义
4.2解决措施
5.多线程通信
6.线程池的掌握
6.1核心参数
6.2常见线程池
6.3自定义线程池
6.4操作步骤
1.基本概念
线程状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、
等待(Waiting)、超时等待(Timed Waiting)、终止(Terminated)
锁(同步监视器):分为悲观锁和乐观锁,常见的有偏向锁、轻量级锁、重量级锁
CAS(Compare And Swap)是一种乐观锁的实现方式,通过比较并
交换来实现并发控制
2.创建线程方式
Runnable接口、Callable接口、Future接口、FutureTask类
2.1直接建立线程
创建Thread子类,按需求重写run()方法 【run代表此线程执行时会做的事】
这里的属性若为 多线程共享属性,加static修饰
2.2实现Runnable接口
这里的 多线程共享属性 可以是非静态的,因为多线程共用此接口
3.3实现Callable接口
(1)实现接口Callable,重写call()回调方法,会返回一个值,值类型由Callable泛型决定
实例化上述类,利用FutureTask(Callable)构造器实例化类
3.4 了解Future接口
异步调用的多线程开发模式之一(异步:当我们需要调用一个函数方法时,并不急着要结果。让被调者立即返回,让它在后台慢慢处理这个请求;此时则可以先处理一些其他任务)
Future模式主要角色及其作用
Main->调用Client发送请求
Data->返回数据的接口
Client->返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData
FutureData->虚拟数据,伪造数据立即返回,最终装配上RealData
RealData->真实数据,构造缓慢
3.5实例化FutureTask类
FutureTask用于异步获取执行结果或取消执行任务
通过传入Runnable或者Callable的任务给FutureTask,
直接调用其run方法或者放入线程池执行,
最后在外部通过FutureTask的get方法异步获取执行结果(适合耗时操作)
3.实现线程安全
3.1定义
多线程环境下,对共享资源的访问不会导致数据出错。
因此和单线程执行相同的操作,结果相同
3.2不安全原因
1. 线程是抢占式的执行,线程间的调度充满了随机性
2. 多个线程对同一个变量进行修改操作
3. 对变量的操作不是原子性的
4. 内存可见性导致的线程安全问题
5. 指令重排序也会影响线程安全
3.3解决方案
使用同步机制(如synchronized、Lock)、
使用线程安全的数据结构(如ConcurrentHashMap)
synchronized:Java关键字,属于隐式锁,可以修饰方法或代码块
Lock:JAVA接口,显式锁
原理上都是通过对共享资源加锁来实现同步。
3.4volatile与synchronized区别
volatile关键字用于保证变量的可见性
而synchronized既保证可见性又保证原子性
volatile适用于单个变量的读写操作,而synchronized适用于复合操作或临界区。
4.极端情况——线程死锁
4.1定义
多个线程相互等待对方释放资源,导致无法继续执行
4.2解决措施
避免嵌套锁、
按固定的顺序获取锁、
设置超时时间、
使用Lock对象代替synchronized关键字。
5.多线程通信
通过共享变量、wait()和notify()、BlockingQueue等机制来实现线程间的数据交换和协作
6.线程池的掌握
6.1核心参数
核心线程数(corePoolSize):依据任务的处理时间和每秒产生的任务数量来确定
最大线程数(maximumPoolSize):参照核心线程数和系统每秒产生的最大任务数决定
线程空闲时间(keepAliveTime):用户自设置合理时间间隔
任务队列长度:(workQueue):大于等于任务队列长度,就丢弃多的任务
6.2常见线程池
FixedThreadPool、CachedThreadPool、ScheduledThreadPool
6.3自定义线程池
1:编写任务类(MyTask),实现Runnable接口;
2:编写线程类(MyWorker),用于执行任务,需要持有所有任务;
3:编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;
4:编写测试类(MyTest),创建线程池对象,提交多个任务测试;
如下模板:
package com.itheima.demo01;import java.util.Collections;
import java.util.LinkedList;
import java.util.List;/*这是自定义的线程池类;成员变量:1:任务队列 集合 需要控制线程安全问题2:当前线程数量3:核心线程数量4:最大线程数量5:任务队列的长度成员方法1:提交任务;将任务添加到集合中,需要判断是否超出了任务总长度2:执行任务;判断当前线程的数量,决定创建核心线程还是非核心线程*/
public class MyThreadPool {// 1:任务队列 集合 需要控制线程安全问题private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());//2:当前线程数量private int num;//3:核心线程数量private int corePoolSize;//4:最大线程数量private int maxSize;//5:任务队列的长度private int workSize;public MyThreadPool(int corePoolSize, int maxSize, int workSize) {this.corePoolSize = corePoolSize;this.maxSize = maxSize;this.workSize = workSize;}//1:提交任务;public void submit(Runnable r){//判断当前集合中任务的数量,是否超出了最大任务数量if(tasks.size()>=workSize){System.out.println("任务:"+r+"被丢弃了...");}else {tasks.add(r);//执行任务execTask(r);}}//2:执行任务;private void execTask(Runnable r) {//判断当前线程池中的线程总数量,是否超出了核心数,if(num < corePoolSize){new MyWorker("核心线程:"+num,tasks).start();num++;}else if(num < maxSize){new MyWorker("非核心线程:"+num,tasks).start();num++;}else {System.out.println("任务:"+r+" 被缓存了...");}}}
6.4操作步骤
1:利用Executors工厂类的静态方法,创建线程池对象;
2:编写Runnable或Callable实现类的实例对象;
3:利用ExecutorService的submit方法或ScheduledExecutorService的schedule方 法提交并执行线程任务
4:如果有执行结果,则处理异步执行结果(Future)
5:调用shutdown()方法,关闭线程池