2. 同步弊端
- 影响效率
- 如果出现了嵌套锁,容易产生死锁
3. 死锁
- 死锁:死锁是指两个以上的线程在执行过程中,因为争夺资源而产生的一种相互等待的现象
二、线程间的通信
1. wait()
-
导致当前线程处于等待状态
-
在调用 wait方法之前,当前线程必须拥有此对象监视器(锁对象),换句话说,必须在 锁对象 上调用 wait方法(此方法只应该由作为此对象监视器的所有者的线程 来调用)
-
一旦在锁对象上调用了 wait方法,紧接着:
-
当前线程放弃 cpu 执行权,并等待
-
放弃持有的 锁对象
相同点:使当前线程放弃cpu执行权,处于阻塞状态
不同点:
- 线程因为 sleep() 方法 处于阻塞状态的时候,不会放弃所持有的锁对象;线程因为wait() 处于阻塞状态的时候,会放弃锁对象
- 使用条件: sleep() 没有任何特殊条件; 使用 wait() 则必须持有锁,在锁对象上调用**wait()**方法
- 唤醒条件:sleep() 的唤醒条件是休眠时间结束;wait() 被唤醒,只能是在其它线程中调用了同一个锁对象的 notify() 或者 **notifyAll()**方法
2. nofity()
- 唤醒在此对象(调用wait的同一个锁对象) 监视器上等待的单个线程;如果所有线程都在此对象上等待,则会选择唤醒其中一个线程
- 直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程
- 被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争
3. notifyAll()
- 唤醒在此对象监视器上等待的所有线程
三、线程池
1. 概述
- 我们创建一个线程,只能使用一次
- 线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源
- 线程池的原理:线程池里每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一次被使用
为什么要使用线程池?
- 在 java 中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。
- 除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。
- 为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
- 线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
2. 创建线程池
-
通常,线程池都是通过 线程池工厂 创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法Executors:线程池创建工厂类
-
创建一个可根据需要创建新线程 的线程池,但是在以前构造的线程可用时将重用它们 (可变)
-
对于执行很多短期异步(短但是频繁) 任务的程序而言,这些线程池通常可提高程序性能
-
如果现有线程没有可用的,则创建一个新线程并添加到池中
-
终止并从缓存中移除那些已有 60 秒钟未被使用的线程(折中)
-
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程
-
如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待
-
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程
-
可保证顺序地执行各个任务
3. 提交任务
- Runnable 接口
- Callable 接口类似于 Runnable,用来指定线程的任务。其中的 call() 方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
4. 使用线程池中线程对象的步骤:
- 创建线程池对象
- 创建 Runnable 接口/Callable接口 子类对象
- 提交 Runnable 接口/Callable接口 子类对象
- 关闭线程池
public class ThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建一个newCachedThreadPool
ExecutorService executorService = Executors.newFixedThreadPool(5);
//操作线程池
//向线程池提交任务
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(“hello, thread pool”);
}
});
//Callable接口的使用
Future future = executorService.submit(new Callable() {
@Override
public Integer call() throws Exception {
TimeUnit.SECONDS.sleep(3);
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
return sum;
}
});
System.out.println(future);
System.out.println(future.get());
}
}
四、定时器 Timer
1. 概述
- 调度定时任务,帮助我们在稍后的时刻执行定时任务。一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务 执行一次,或者 定期重复执行
2. TimerTask 定时任务
- 在 timer 中所有的定时任务都是运行在同一个线程中
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip1024b 备注Java获取(资料价值较高,非无偿)
独家面经总结,超级精彩
本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家!
Java面试准备
准确的说这里又分为两部分:
- Java刷题
- 算法刷题
Java刷题:此份文档详细记录了千道面试题与详解;
va面试准备
准确的说这里又分为两部分:
- Java刷题
- 算法刷题
Java刷题:此份文档详细记录了千道面试题与详解;
[外链图片转存中…(img-fjzFkfCc-1711552961383)]
[外链图片转存中…(img-MSRhQbXy-1711552961383)]