多线程
1.实现多线程
1.1并发和并行【理解】
-
并行:在同一时刻,有多个指令在多个CPU上同时执行。
-
并发:在同一时刻,有多个指令在单个CPU上交替执行。
1.2进程和线程【理解】
-
进程:是正在运行的程序独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的并发性:任何进程都可以同其他进程一起并发执行
-
线程:是进程中的单个顺序控制流,是一条执行路径 单线程:一个进程如果只有一条执行路径,则称为单线程程序 多线程:一个进程如果有多条执行路径,则称为多线程程序
1.3实现多线程方式一:继承Thread类
-
实现步骤
- 定义一个类MyThread继承Thread类
- 在MyThread类中重写run方法
- 创建MyThread类的对象
- 启动线程
1.4实现多线程方式二:实现Runnable接口
-
实现步骤
- 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
- 启动线程
1.5实现多线程方式三: 实现Callable接口
-
实现步骤
- 定义一个类MyCallable实现Callable接口
- 在MyCallable类中重写call()方法
- 创建MyCallable类的对象
- 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
- 创建Thread类的对象,把FutureTask对象作为构造方法的参数
- 启动线程
- 再调用get方法,就可以获取线程结束之后的结果。
-
三种实现方式的对比
- 实现Runnable、Callable接口
- 好处: 扩展性强,实现该接口的同时还可以继承其他的类
- 缺点: 编程相对复杂,不能直接使用Thread类中的方法
- 继承Thread类
- 好处: 编程比较简单,可以直接使用Thread类中的方法
- 缺点: 可以扩展性较差,不能再继承其他的类
- 实现Runnable、Callable接口
1.6线程优先级(概率)
-
getPriority() 返回此线程的优先级
-
setPriority() 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1(MIN)-10(MAX)
-
yield() 把线程让出来 重新抢
-
join() 把这个线程先执行
1.7守护线程 (备胎线程)
setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将陆续退出
2.线程同步
2.1同步代码块
把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
synchronized(任意对象) { 多条语句操作共享数据的代码}2.2同步方法
默认锁为类名.class
修饰符 synchronized 返回值类型 方法名(方法参数) { 方法体;}3.Lock锁
Lock本身是接口 所以要调用他的实现类ReentrantLock
方法lock(),unlock()
避免死锁DeadLock的发生
TIP多线程的4步套路:
循环
同步代码块
判断共享数据是否到末尾(到了末尾)
判断共享数据是否到末尾(没到,执行核心逻辑)
4.等待唤醒机制
4.1消费者生产者代码 wait() notify()
4.2.阻塞队列实现
ArrayBlockingQueue put() take()
LinkedBlockingQueue
5.线程的6大状态

红色为自填加 方便理解 实际过程中会交给操作系统进行处理
6线程池
1.线程池存在的意义:
系统创建一个线程的成本是比较高的,因为它涉及到与操作系统交互,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理是对系统资源的消耗,这样就有点”舍本逐末”了。
针对这一种情况,为了提高性能,我们就可以采用线程池。线程池在启动的时,会创建大量空闲线程,当我们向线程池提交任务的时,线程池就会启动一个线程来执行该任务。等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。
2.线程池的设计思路 :
- 准备一个任务容器
- 一次性启动多个(2个)消费者线程
- 刚开始任务容器是空的,所以线程都在wait
- 直到一个外部线程向这个任务容器中扔了一个”任务”,就会有一个消费者线程被唤醒
- 这个消费者线程取出”任务”,并且执行这个任务,执行完毕后,继续等待下一次任务的到来
6.1 线程池-Executors默认线程池
在真实企业开发中我们也很少去自定义线程池,而是使用JDK中自带的线程池,我们可以使用Executors中所提供的静态方法来创建线程池
-
static ExecutorService newCachedThreadPool() 创建一个默认的线程池
-
static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池
然后提交任务 submit( new MyRunnable () or new MyCallable() )
最后可以销毁线程池 shutdown();
6.2 自定义线程池-ThreadPoolExecutor
参数详解

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) corePoolSize: 核心线程的最大值,不能小于0 maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize keepAliveTime: 空闲线程最大存活时间,不能小于0 unit: 时间单位 workQueue: 任务队列,不能为null threadFactory: 创建线程工厂,不能为null handler: 任务的拒绝策略,不能为null非默认任务拒绝策略
RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,它下面存在4个子类。
-
ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
-
ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法。
-
ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中。
-
ThreadPoolExecutor.CallerRunsPolicy: 调用任务的run()方法绕过线程池直接执行。
明确线程池对多可执行的任务数 = 队列容量 + 最大线程数
TIP不断的提交任务,会有以下三个临界点: 当核心线程满时,再提交任务就会排队 当核心线程满,队伍满时,会创建临时线程 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略
补充
每个线程都有自己独立的栈 在没学习多线程是 默认是只有一个main栈 ps :堆是唯一的
还有很多额外内容 后面有机会再看 笔记在本地就不传了












