线程
线程是操作系统能够进行运算调度的最小单位。它被包含再进程之中,是进程中的最小执行单位。
进程:是程序的基本执行实体。
什么是多线程?
有了多线程,我们就可以让程序同时做多件事情(指一个进程运行时,多个线程并行交替执行的过程)。
多线程的作用?
提高效率。
多线程的应用场景?
只要你想让多个事情同时运行就需要用到多线程;
比如:软件中的耗时操作,所有的聊天软件、所有的服务器。
多线程的两个概念:
并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
多线程的实现方式:
- 继承thread类的方式进行实现
- 实现Runnable接口的方式进行实现
- 利用Callable接口和Future接口方式实现
多线程三种实现方式对比
优点 | 缺点 | |
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 可扩展性较差,不能再继承其他的类 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他的类 | 编程相对复杂,不能直接使用Thread类中的方法 |
实现Callable接口 |
线程的优先级
方法名称 | 说明 |
String getName() | 返回此线程的名称 |
void setName(String name) | 设置线程的名字(构造方法也可以设置名字) |
static Thread currentThread()()())( | 获取当前线程的对象 |
static void sleep(long time) | 让线程休眠指定的时间,单位为毫秒 |
setPriority(int newPriority) | 设置线程的优先级 |
final int getPriority() | 获取线程的优先级 |
final void setDaemon(boolean on) | 设置为守护线程 |
public static void yield() | 出让线程/礼让线程 |
public static void join() | 插入线程/插队线程 |
线程的调度:抢占式调度---随机性、非抢占式调度---顺序性
线程的优先级:1-10,优先级越大,抢到的概率就越大
守护线程
出让线程/礼让线程
插入线程/插队线程
线程的生命周期
线程的安全问题(使用同步代码块解决)
- 相同的票出现了多次(因为线程执行时,具有随机性)
- 出现了超出范围的票(因为线程执行时,具有随机性)
解决问题1的方法:数据用static修饰,使数据共享
解决问题2 超卖的方法:加锁
注意点:
- 锁一定要放在循环的里边;
- 锁对象,一定要是唯一的。
同步方法
- 就是把synchronized关键字加到方法上
特点:
- 同步方法是锁住方法里面所有的代码
- 锁对象不能自己指定-------非静态:this;静态:当前类的字节码文件对象
StringBuilder:单线程的情况,不需要考虑数据安全的情况
StringBuffer:多线程的情况,需要考虑数据安全
Lock锁
- synchronized锁:自己开关锁
- Lock锁:需要手动开关锁
死锁(是一个错误,要避免):锁与锁之间形成了嵌套
生产者和消费者(等待唤醒机制)
等待唤醒机制(阻塞队列方式实现)
线程的状态(6种)
- 新建状态(NEW):创建线程对象
- 就绪状态(RUNNABLE):start方法
- 阻塞状态(BLOCKED):无法获得锁对象
- 等待状态(WAITING):wait方法
- 计时等待(TIMED_WAITING):sleep方法
- 结束状态(TERMINATED):全部代码运行完毕
1、NEW:新建状态,线程被创建出来,但尚未启动时的线程状态
2、RUNNABLE:就绪状态,表示可以运行的线程状态,但它在排队等待来自操作系统的CPU资源 3、BLOCKED:阻塞等待锁的线程状态,表示正在处于阻塞状态的线程,正在等待监视器锁, 比如等待执行 synchronized 代码块或者使用 synchronized 标记的方法
4、WAITING:等待状态,一个处于等待状态的线程正在等待另一个线程执行某个特定的动作。 例如,一个线程调用了Object.wait() 它在等待另一个线程调用 Object.notify() 或 Object.notifyALL() 5、TIMED_WAITING:计时等待状态,和等待状态(WAITING)类似,只是多了超时时间, 比如调用了有超时时间设置的方法 Object.wait(long timeout)和Thread.join(long timeout)就会进入此状态
6、TERMINATED:终止状态:表示线程已经执行完成
线程池主要核心原理
- 创建一个池子,池子中是空的
- 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程的即可。
- 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
线程池代码实现
Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。
方法名称 | 说明 |
public static ExecutorService newCachedThreadPool() | 创建一个没有上限的线程池 |
public static ExecutorService newFixedThreadPool(int nThreads) | 创建有上限的线程池 |
线程复用
临时线程:当核心线程全都占用且等待队列中的数量已经超过设定的阈值的时候,才会触发临时线程的创建。
自定义线程池
- 核心线程数量:不能小于0
- 线程池中最大线程的数量:最大数量>=核心线程数量
- 空闲时间(值):不能小于0
- 空闲时间(单位):用TimeUnit指定
- 阻塞队列:不能为null
- 创建线程的方式:不能为null
- 要执行的任务过多时的解决方案(拒绝服务):不能为null
自定义线程池(任务拒绝策略)
任务拒绝策略 | 说明 |
ThreadPoolExecutor.AbortPolicy | 默认策略:丢弃任务并抛出RejectedExecutionException异常 |
ThreadPoolExecutor.DiscardPolicy | 丢弃任务,但是不抛出异常 这是不推荐的做法 |
ThreadPoolExecutor.DiscardOldestPolicy | 抛弃队列中等待最久的任务 然后把当前任务加入队列中 |
ThreadPoolExecutor.CallerRunsPolicy | 调用任务的run()方法绕过线程池直接执行 |
不断的提交任务,会有以下三个临界点:
- 当核心线程满时,再提交任务就会排队
- 当核心线程满,队伍满时,会创建临时线程
- 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略