3.5 多线程
2021/4/13 18:29:26
本文主要是介绍3.5 多线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
5.1 线程与进程
5.1.1 概念
- 进程:一个内存中运行的应用程序,每个进程都有一个独立的内存空间
- 线程:进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
5.1.2 线程调度
- java采用抢占式调度。多线程不能提高运行速度,可以提高运行效率,提高CPU使用率。
5.1.3 同步与异步
- 同步:排队执行,效率低但是安全
- 异步:同时执行,效率高但是不安全
5.1.4 并发与并行(面试题)
- 并发:两个或多个事件在同一个时间段内发生
- 并行:两个或多个事件在同一时刻发生
5.2 多线程
5.2.1 实现途径
- 继承Thread类
- 重写run方法,通过start方法调用
- 实现Runnable接口
- 重写run方法,创建一个线程并为其分配任务
- 实现Callable接口(较少使用)
// 1. 编写类实现Callable接口 , 实现call方法 class XXX implements Callable<T> { @Override public <T> call() throws Exception { return T; } } // 2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象 FutureTask<Integer> future = new FutureTask<>(callable); // 3. 通过Thread,启动线程 new Thread(future).start();
Runnable与Callable异同
- 相同点:
- 都是接口
- 都可以编写多线程
- 都采用Thread.start()启动线程
- 不同点:
- Runnable没有返回值;Callable可以返回执行结果
- Callable接口的call()允许抛出异常;Runnable的run()不能抛出
Callable获取返回值
- 调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
5.2.1.1 Runnable比Thread的优势
- 更适合多个线程执行相同任务的情况
- 避免单继承带来的局限性
- 任务与线程分离,提高程序健壮性
- 线程池技术只接受Runnable类型任务,不接受Thread线程
5.2.2 线程中断
- 提醒线程该中断
- 添加中断标记:interrupt
5.2.3 进程同步方法
- 法一:同步代码块
- run()方法内通过将同一对象传入synchronized(),可调用this关键字或提前实例化一个对象
- 法二:同步方法
- 方法权限修饰符后添加synchronized,通过同一任务不同线程的start方法调用
* 若添加了同步代码块和同步方法,若某一线程抢到资源,则其他线程不能执行同步方法中的内容
- 法三:显式锁
- run()方法前创建锁对象 Lock l=new ReentrantLock();
- 上锁:l.lock(); 解锁:l.unlock();
5.2.3 显式锁和隐式锁区别
- 两者出身不同
- synchronized是Java中的关键字,由JVM维护,是JVM层面的锁;
- lock是JDK5之后才出现的具体的类,使用Lock是调用对应的API,是API层面的锁。
- 使用方式不同
- synchronized隐式锁;lock是显式锁
- 显式锁和隐式锁的区别在于:使用显式锁的时候,使用者需要手动去获取和释放锁。如果没有释放锁,就可能出现死锁的现象
- 加锁的时候是否可以公平
- Sync:非公平锁
- lock:默认是非公平锁。在其构造方法的时候可以传入Boolean值。true:公平锁
(原文链接:https://zhuanlan.zhihu.com/p/131286865)
5.2.4 线程阻塞
- 在某一时刻某一个线程在运行一段代码的时候,这时候另一个线程也需要运行,但是在运行过程中的那个线程执行完成之前,另一个线程是无法获取到CPU执行权的,这个时候就会造成线程阻塞。
5.2.5 公平锁和非公平锁
- 公平锁:保证获取锁的线程按照先来后到的顺序
- 非公平锁:获取锁的顺序和申请锁的顺序不一定一致
5.2.6 死锁
- 两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁。
5.2.7 线程六种状态
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(ready)和**运行中(running)****两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。- 阻塞(BLOCKED):在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
- 等待(WAITING):处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。(wait方法)
- 超时等待(TIMED_WAITING):处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。(sleep方法)
- 终止(TERMINATED):表示该线程已经执行完毕。
5.2.8 线程池(较少使用)
5.2.8.1 分类
- 缓存线程池
- 定长线程池
- 单线程线程池
- 周期性任务定长线程池
5.2.8.2 应用
/*** 缓存线程池. * (长度无限制) * 执行流程: * 1. 判断线程池是否存在空闲线程 * 2. 存在则使用 * 3. 不存在,则创建线程 并放入线程池, 然后使用 */ ExecutorService service = Executors.newCachedThreadPool(); //向线程池中 加入 新的任务 service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } });
/*** 定长线程池. * (长度是指定的数值) * 执行流程: * 1. 判断线程池是否存在空闲线程 * 2. 存在则使用 * 3. 不存在,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 * 4. 不存在,且线程池已满的情况下,则等待线程池存在空闲线程 */ ExecutorService service = Executors.newFixedThreadPool(2); //线程池个数 service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } });
//效果与定长线程池 创建时传入数值1 效果一致. /*** 单线程线程池. * 执行流程: * 1. 判断线程池 的那个线程 是否空闲 * 2. 空闲则使用 * 3. 不空闲,则等待 池中的单个线程空闲后 使用 */ ExecutorService service = Executors.newSingleThreadExecutor(); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } });
/*** 周期任务 定长线程池. * 执行流程: * 1. 判断线程池是否存在空闲线程 * 2. 存在则使用 * 3. 不存在,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 * 4. 不存在,且线程池已满的情况下,则等待线程池存在空闲线程 */ /*** 格式1: * 参数1. runnable类型的任务 * 参数2. 时长数字 * 参数3. 时长数字的单位 */ ScheduledExecutorService service = Executors.newScheduledThreadPool(1); service.schedule(new Runnable() { @Override public void run() { System.out.println("xxx"); } },5,TimeUnit.SECONDS); //5s后执行 /*** 格式2: * 参数1. runnable类型的任务 * 参数2. 时长数字(延迟执行的时长) * 参数3. 周期时长(每次执行的间隔时间) * 参数4. 时长数字的单位 */ service.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("俩人相视一笑~ 嘿嘿嘿"); } },5,2,TimeUnit.SECONDS);//5s后执行,每隔2s执行
5.3 Lambda表达式
5.3.1 概念
- Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类.
5.3.2 作用与要求
- 对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法
- @FunctionalInterface
修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。- 语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)
这篇关于3.5 多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-23线下车企门店如何实现线上线下融合?
- 2024-12-23鸿蒙Next ArkTS编程规范总结
- 2024-12-23物流团队冬至高效运转,哪款办公软件可助力风险评估?
- 2024-12-23优化库存,提升效率:医药企业如何借助看板软件实现仓库智能化
- 2024-12-23项目管理零负担!轻量化看板工具如何助力团队协作
- 2024-12-23电商活动复盘,为何是团队成长的核心环节?
- 2024-12-23鸿蒙Next ArkTS高性能编程实战
- 2024-12-23数据驱动:电商复盘从基础到进阶!
- 2024-12-23从数据到客户:跨境电商如何通过销售跟踪工具提升营销精准度?
- 2024-12-23汽车4S店运营效率提升的核心工具