线程
线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运用单位(简单理解:应用软件中相互独立,可以同时运行的功能)
并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
多线程的实现方式
- 继承Thread类的方式实现
- 继承Thread类
- 重写Thread类中的run方法
- 实现Runnable接口的方式进行实现
- 自己定义一个类实现Runnable接口
- 重写里面的run方法
- 创建自己的类的对象
- 创建一个Thread类的对象,并开启线程
- 利用Callable接口和Future接口方式实现(特点:可以获取多线程运行的结果)
- 创建一个类MyCallable实现Callable接口
- 重写call(是有返回值的,表示多线程运行的结果)
- 创建MyCallable的对象(表示多线程要执行的任务)
- 创建FutureTasak的对象(作用管理多线程运行的结果)
- 创建Thread类的对象,并启动(表示线程)
优点 | 缺点 | |
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 扩展性较差,不能再继承其他类 |
实现Ruunable接口 | 扩展性强,实现该接口的同时还可以继承其他类 | 编程相对复杂,不能直接使用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() | 插入线程/插队线程 |
名字:
- 如果没有给线程设置名字,线程也是有默认的名字的(格式: Thread-X(X序号,从0开始的))
- 如果我们要给线程起名字,可以用set方法,也可以构造方法设置
sleep:
- 那条线程执行到这个方法,那么那条线程就会在这里停留对应的时间
- 方法的参数:表示睡眠的时间,单位毫秒
- 当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
守护线程:当其他非守护线程执行完毕之后,守护线程也没有存在的必要了
线程的生命周期
线程的安全问题
同步代码块
把操作共享数据的代码锁起来
格式:
synchronized (锁) {
操作共享数据的代码
}
特点1:锁默认打开,有一个线程进去的了,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开
锁对象:一定要是唯一的
同步方法
就是把synchronized关键字加到方法上
格式:
修饰符 synchronized 返回值类型 方法名(方法参数) {...}
特点1:同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定(非静态:this, 静态:当前类的字节码文件对象)
书写套路:
1.循环
2.同步代码块
3.判断共享数据是否到了末尾,如果到了末尾
4.判断共享数据是否到了末尾,如果没有到末尾
Lock锁
Lock中提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
`ReentrantLock():创建一个 ReentrantLock的实例
生产者和消费者(等待唤醒机制)
消费者:
- 判断桌子上是否有食物
- 如果没有就等待
- 如果有就开吃
- 吃完之后,唤醒厨师继续做
生产者
- 判断桌子上是否有食物
- 有:等待
- 没有:制作食物
- 把食物放在桌子上
- 叫醒等待的消费者开吃
方法名称 | 说明 |
void wait() | 当前线程等待,直到被其他线程唤醒 |
void notify() | 随机唤醒单个线程 |
void notifyAll() | 唤醒所有线程 |
阻塞队列的继承结构
线程池
主要原理:
- 创建一个池子,池子中是空的
- 提交任务,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
- 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
线程池代码实现
- 创建线程池
- Executors:线程池的工具类通过调用方法返回不同类型的线程池对象
方法名称 说明 public static ExecuroeService newCachedThreadPool() 创建一个没有上线的线程池 public static ExecutorService newFixedThreadPool(int nThreads) 创建有上限的线程池
- Executors:线程池的工具类通过调用方法返回不同类型的线程池对象
- 提交任务
- 所有的任务全部执行完毕,关闭线程池
自定义线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
(核心线程数量, 最大线程数, 空闲线程最大存活时间, 时间单位, 任务队列, 创建线程工厂, 任务的拒绝策略)
参数一:核心线程数量 不能小于0
参数二:最大线程数 不能小于等于0,最大数量 >= 核心线程数量
参数三:空闲线程最大存活时间 不能小于0
参数四: 时间单位 用TimeUnit指定
参数五:任务队列 不能为null
参数六:创建线程工厂 不能为null
参数七:任务的拒绝策略 不能为null
线程池多大合适
项目类型:
- CPU密集型运算(计算比较多.读取数据比较少) : 最大并行数 (可用 Runtime.getRuntime().availableProcessors()方法获取)+ 1
- I / O密集型运算(读取本地文件较多或读取数据库比较多): 最大并行数 * 期望CPU利用率 * 总时间(CPU计算时间+等待时间) / (CPU计算时间)