一。线程概念:是进程的实体,是CPU调度和分配比进程更小的调度单位。
进程:是一个具有独立功能的程序在某个数据集合的一次运行活动,是操作系统进行资源分配和调度的独立单位。
线程小于进程,让多进程有高并发的特性,进程在运行的时候每个单元模块相互独立,线程之间按内存共享,优化用户体验。
二.创建线程的4种方式
1,继承Thread 类重写run方法创建线程,但是不能继承其他类;
2,实现Runnable接口重写run方法,避免单继承(接口方法没有返回值,只能抛出运行时异常无法捕获)
3,实现Callable接口重写Call方法,可以获取线程结果的返回值,并且可以抛出异常(接口方法有返回值,支持范型)
4,使用线程池创建 java.util.concurrent.Executor接口
三.启动新线程时,start 和 run方法区别?
run :线程对象调用run 方法不开启线程,仅仅是对象调用方法,并且run方法只是Thread的一个普通方法,在主线程中执行。
start:对象调用start方法可以启动线程,并让jvm调用run方法在开启的线程中执行,使线程进入就绪状态。
四.线程的五种状态以及转换
新建--就绪--运行--阻塞--死亡
其中就绪状态下有三种
1)等待,wait()方法,线程等待某种工作完成
2)超时等待,sleep(),等到处理完毕后线程才会重新转入就绪状态
3)同步阻塞,因为锁被其他线程占用,所以会进入同步阻塞
线程相关的基本方法?6个
1.wait 线程等待,只有等等另外线程的通知或者被中断才会返回,在调用wait()方法后会释放对象的锁,所以一般用在同步方法或者同步代码块当中。
2.sleep 线程睡眠,不会释放当前占有的锁,会导致线程进入TIMED-WATIN状态。
3.yield 线程让步,会使当前线程让出CPU执行时间片,和其他线程一起重新竞争CPU时间片,但是 有些操作系统对线程优先级不敏感。
4.interrupt 线程中断,也就是给线程一个通知信号,会影响这个线程的一个中断标识位,这个线程本身不会因此而改变。
5.join 等待其他线程终止,join()方法,等待其他线程终止,在当前线程中调用一个线程的join方法,当前线程会转化为阻塞状态,回到另一个线程结束,当前线程会和由阻塞变为就绪
6.notify 线程唤醒,Object 类中的 notify() 方法,唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意的,并在对实现做出决定时发生,线程通过调用其中一个 wait() 方法,在对象的监视器上等待,直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。类似的方法还有 notifyAll() ,唤醒再次监视器上等待的所有线程。
wait 来自Object | sleep来自Thread |
等待过程中会释放锁 | 等待过程中不会释放锁 |
在同步代码块中使用 | 任何地方都可以 |
不需要捕获异常 | 需要捕获异常 |
线程池
使用线程池的原因:可以重复利用已经有的线程继续执行,避免线程在 创建销毁时造成的消耗,可以提高系统的响应速度。
线程池的分类:
- newCachedThreadPool:创建一个可进行缓存重复利用的线程池
- newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,线程池中的线程处于一定的量,可以很好的控制线程的并发量
- newSingleThreadExecutor:创建一个使用单个 worker 线程的Executor ,以无界队列方式来运行该线程。线程池中最多执行一个线程,之后提交的线程将会排在队列中以此执行
- newSingleThreadScheduledExecutor:创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期执行
- newScheduledThreadPool:创建一个线程池,它可安排在给定延迟后运行命令或者定期的执行
- newWorkStealingPool:创建一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如不传并行级别参数,将默认为当前系统的CPU个数
线程池核心参数:
核心线程池大小:corePoolSize
线程池创建线程的最大个数:maximumPoolSize
空闲线程存活时间:keepAliveTime 指定的时间单位:unit
阻塞队列:WorkQueue:用于保存任务的阻塞队列
创建线程的工程类:treadeFactory
饱和策略(拒绝策略):handler
线程池原理:
当一个任务提交到线程池后,
1. 线程池首先判断核心线程池里的线程是否已经满了。如果不是,则创建一个新的工作线程来执行任务。否则进入2.
2. 判断工作队列是否已经满了,倘若还没有满,将线程放入工作队列。否则进入3.
3. 判断线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行。如果线程池满了,则交给饱和策略来处理任务
关闭线程池?
可以通过shutdown 和 shutdownNow 两个方法
原理:遍历线程池所有线程,然后依次中断
1,shutdown只是将线程池的状态设置为SHUTDOWN状态,然后中断所有没有正在执行任务的线程
2,shutdownNow首先将线程池的状态设置为STOP,然后尝试停止所有的正在执行和未执行任务的线程,并返回等待执行任务的列表
关于拒绝策略?
ThreadPoolExecutor.AbortPolicy(系统默认): 丢弃任务并抛出RejectedExecutionException异常,让你感知到任务被拒绝了,我们可以根据业务逻辑选择重试或者放弃提交等策略
ThreadPoolExecutor.DiscardPolicy: 也是丢弃任务,但是不抛出异常,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。
ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程),通常是存活时间最长的任务,它也存在一定的数据丢失风险
ThreadPoolExecutor.CallerRunsPolicy:既不抛弃任务也不抛出异常,而是将某些任务回退到调用者,让调用者去执行它。