Java-进阶:多线程2,linux软件架构图

三、线程池

四、定时器 Timer

五、多线程和异常

一、Lock 接口

1. 线程锁

  • 是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁

//锁的使用
Lock l = new ReentrantLock(); //创建一个锁对象
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}

  • LockSynchronize更为灵活的功能:

2. 同步弊端

  • 影响效率
  • 如果出现了嵌套锁,容易产生死锁

3. 死锁

  • 死锁:死锁是指两个以上的线程在执行过程中,因为争夺资源而产生的一种相互等待的现象

二、线程间的通信

1. wait()

  • 导致当前线程处于等待状态

  • 在调用 wait方法之前,当前线程必须拥有此对象监视器(锁对象),换句话说,必须在 锁对象 上调用 wait方法(此方法只应该由作为此对象监视器的所有者的线程 来调用)

  • 一旦在锁对象上调用了 wait方法,紧接着:

  • 当前线程放弃 cpu 执行权,并等待

  • 放弃持有的 锁对象

相同点:使当前线程放弃cpu执行权,处于阻塞状态
不同点

  1. 线程因为 sleep() 方法 处于阻塞状态的时候,不会放弃所持有的锁对象;线程因为wait() 处于阻塞状态的时候,会放弃锁对象
  2. 使用条件sleep() 没有任何特殊条件; 使用 wait() 则必须持有锁,在锁对象上调用**wait()**方法
  3. 唤醒条件sleep() 的唤醒条件是休眠时间结束;wait() 被唤醒,只能是在其它线程中调用了同一个锁对象的 notify() 或者 **notifyAll()**方法

2. nofity()

  • 唤醒在此对象(调用wait的同一个锁对象) 监视器上等待的单个线程;如果所有线程都在此对象上等待,则会选择唤醒其中一个线程
  • 直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程
  • 被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争

3. notifyAll()

  • 唤醒在此对象监视器上等待的所有线程

三、线程池

1. 概述

  • 我们创建一个线程,只能使用一次
  • 线程池,其实就是一个容纳多个线程的容器,其中的线程可以**

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

反复使用**,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源

  • 线程池的原理:线程池里每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一次被使用

为什么要使用线程池?

  1. 在 java 中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。
  2. 除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。
  3. 为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
  4. 线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

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 中所有的定时任务都是运行在同一个线程中
  1. cancel 取消定时任务

public class TimerDemo {
public static void main(String[] args) {
//定义一个定时器
// 在timer中所有的定时任务都是运行在同一个线程中
Timer timer = new Timer(); //默认不是守护线程

//在定时器上调度,定时任务
timer.schedule(new MyTimerTask(), 3000);
timer.schedule(new MyTimerTask(), 1000);

//调度器重复执行定时任务
timer.schedule(new MyTimerTask(timer), 0, 2000);
}
}

class MyTimerTask extends TimerTask {
Timer timer;
public MyTimerTask(Timer timer) {
this.timer = timer;
}

@Override

你可能感兴趣的:(程序员,面试,java,后端)