多线程:让程序同时做多件事情
线程:线程是操作系统能够进行运行的最小单位,它被包含在进程之中,是进程中实际运作单位
进程:进程是程序的基本执行实体
应用场景:软件中的耗时操作、加载大量资源文件、拷贝迁移大文件
并发和并行:
并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻有多个指令在多个CPU上同时执行
多线程的实现方式:
(1)继承Thread类的方式进行实现:编程比较简单,可以直接使用Thread类中的方法,可扩展性较差,不能继承其他的类
1.自己定义一个类继承Thread
2.重写run方法
3.创建子类对象,并启动线程
(2)实现Runnable接口的方式进行实现:编程比较简单,可以直接使用Thread类中的方法,可扩展性较差,不能继承其他的类
1.自定义一个类,实现Runnable接口
2.重写里面的run方法
3.创建此类的对象
4.创建一个Thread对象,并启动线程
(3)利用Callable接口和Future接口方式实现:扩展性强,实现该类接口的同时还可以继承其他的类,但是编程相对复杂,不能直接使用Thread类中的方法
可以获取到多线程运行的结果
1.创建一个类实现Callable接口
2.重写call(是有返回值的,表示多线程运行的结果)
3.创建该类的对象(表示多线程要执行的任务)
4.创建Future对象(管理多线程运行的结果)
5.创建Thread类对象,并启动(表示线程)
多线程中的常见成员方法:
方法名称 | 说明 |
String getName() | 返回此线程的名称 |
void setName(String name) | 设置线程的名称 |
static Thread currentThread() | 获取当前线程的对象 |
static void sleep(long time) | 让线程休眠指定的时间,单位为毫秒 |
setPrioirty(int newPriority) | 设置线程的优先级 |
final int getPriority() | 获取线程的优先级 |
final void setDaemon(boolean on) | 设置守护线程 |
public static void yield() | 出让线程/礼让线程 |
public static void join() | 插入线程/插队线程 |
当其他的非守护线程执行完毕后,守护线程会陆续结束
线程的生命周期:
线程的安全问题:
同步代码块:把操作共享数据的代码锁起来
格式:
sychronized (锁){
操作共享数据的代码;
}
特点1:锁默认打开,有一个线程进去后锁自动关闭
特点2:里面的代码全部执行完毕,线程出来后锁自动打开
细节1:sychronized要写在循环的里面
细节2:锁对象一定是唯一的
同步方法:把synchronized关键字加到方法上面
格式:
修饰符 synchronized 返回值类型 方法名(方法参数){...}
同步方法是锁住方法里面的所有代码,并且其锁对象不能自己指定(非静态this,静态是当前类的字节码文件对象)
Lock锁:
JDK5以后提供了一个新的锁对象Lock,实现比使用synchronized方法和语句可以获得更广泛的锁定操作,提供了获得锁和释放锁的方法。分别是void lock()和void unlock()。Lock是接口不能直接实例化,可以采用它的ReentrantLock来实例化。
生产者和消费者(等待唤醒机制):生产者消费者模式是一个十分经典的多线程协作的模式
生产者:生产数据
消费者:消费数据
常见方法:
方法名称 | 说明 |
void wait() | 当前线程等待,直到被其他线程唤醒 |
void notify() | 随机唤醒单个线程 |
void notifyAll() | 唤醒所有线程 |
多线程的状态:
新建状态(NEW):创建线程对象
就绪状态(RUNNABLE):start方法
阻塞状态(BLOCKED):无法获得锁对象
等待状态(WAITING):wait方法
计时等待(TIME_WAITING):sleep方法
结束状态(TERMINATED):全部代码执行完毕
线程池:
以前写多线程的弊端:使用线程的时候需要创建线程,使用完毕后线程就会消失
核心逻辑:
1.创建一个线程池,线程池里面是空的
2.提交任务时,线程池会创建新的线程对象,任务执行完毕后线程归还线程池,下次再提交任务时,不需要创建新的线程,直接使用已有的线程即可
3.如果提交任务时,线程池中没有空闲线程,也无法创建新的线程,任务就会排队等待
代码实现:
1.创建线程池
Executors:线程池的工具类通过调用方法返回不同类型的线程池对象
方法名称 | 说明 |
public static ExecutorService newCachedThreadPool() | 创建一个没有上限的线程池 |
public static ExecutorService newFixedThreadPool(int nThreads) | 创建有上限的线程池 |
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(七个参数)
序号 | 参数说明 | 取值范围 |
1 | 核心线程数量 | 不能小于0 |
2 | 最大线程数 | 不能小于等于0,最大数量≥核心线程数量 |
3 | 空闲线程最大存储时间 | 不能小于0 |
4 | 时间单位 | 用TimeUnit指定 |
5 | 任务队列 | 不能为null |
6 | 创建线程工厂 | 不能为null |
7 | 任务的拒绝策略 | 不能为null |
2.提交任务
线程池对象.submir(线程对象)
不断地提交任务,会有以下三个临界点:
(1)当核心线程满时,再提交任务就会排队
(2)当核心线程满,队伍满时,会创建临时线程
(3)当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略
3.所有任务执行完毕,关闭线程池
线程池大小:
CPU密集型运算:最大并行数(x核y线程中的y) + 1
I/O密集型运算:最大并行数 * 期望CPU利用率 * 总时间(CPU计算时间+等待时间)/CPU计算时间