java多线程面试题

news/2024/11/28 11:55:38/

一.java创建多线程的几种常用方式

1.继承Thread类

创建一个线程类并继承Thread ,并覆盖run方法,然后在启动线程的时候调用start

public class MyThread extends Thread {public MyThread() {}public void run() {for(int i = 0; i < 10; i ++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}public static void main(String[] args) {MyThread myThread1 = new MyThread();MyThread myThread2 = new MyThread();MyThread myThread3 = new MyThread();myThread1.start();myThread2.start();myThread3.start();}
}

2.实现Runnable接口

创建一个线程类并实现Runnable接口 ,并实现run方法,然后在启动线程的时候调用start

public class MyRunable implements Runnable{public void run() {for(int i = 0; i < 10; i ++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}public static void main(String[] args) {MyRunable myRun = new MyRunable();MyThread myThread1 = new Thread(myRun);MyThread myThread2 = new Thread(myRun);MyThread myThread3 = new Thread(myRun);myThread1.start();myThread2.start();myThread3.start();}
}

3.实现Callable接口

创建一个线程类,实现Callable接口,并实现call方法,然后在启动线程的时候调用start
callable接口是有返回值的,借助FutureTask类能够实现判断任务是否中断,中断任务以及任务是否完成

public class MyCallnable implements Callable<String> {public String call() throws Exception {for (int i = 0; i < 10; i++) {System.out.println("myCallable线程正在执行:"+i);}return "MyCallabe线程执行完毕";}public static void main(String[] args) {//创建futuretask对象FutureTask<String> futureTask = new FutureTask<String>(new MyCallnable());//创建Thread对象,传入futureTaskThread thread1 = new Thread(futureTask);Thread thread2 = new Thread(futureTask);Thread thread3 = new Thread(futureTask);thread1.start();thread2.start();thread3.start();}
}

4.Executors利用线程池来创建线程

Executors提供五种线程池,分别为:

  • newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
  • newWorkStealingPool:创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行。

下面我们就来重点说下线程池

二.单线程的弊端

  • 每次创建线程,销毁线程性能差,占用系统资源。
  • 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
  • 缺乏更多功能,如定时执行、定期执行、线程中断、线程状态返回等等。

三.线程池

1.什么是线程池

通常我们使用线程的时候就去创建一个线程,执行完了然后再回收释放资源,但是就会有一个问题:
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程释放线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务,其他的任务就可以复用现成的线程资源,不必再创建销毁,浪费系统资源,那么就有了线程池这个东西。

2.线程池有哪些优点

  • 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
  • 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行;
  • 方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场))。
  • 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。

3.Executor中的几个核心类

  • Executor接口:声明了execute(Runnable runnable)方法,执行任务代码
  • ExecutorService接口:继承Executor接口,声明方法:submit、invokeAll、invokeAny以及shutDown等
  • AbstractExecutorService抽象类:实现ExecutorService接口,基本实现ExecutorService中声明的所有方法
  • ScheduledExecutorService接口:继承ExecutorService接口,声明定时执行任务方法
  • ThreadPoolExecutor类:继承类AbstractExecutorService,实现execute、submit、shutdown、shutdownNow方法
  • ScheduledThreadPoolExecutor类:继承ThreadPoolExecutor类,实现ScheduledExecutorService接口并实现其中的方法
  • Executors类:提供快速创建线程池的方法

4.常用线程池

Executors提供五种线程池,分别为:

  • newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
  • newWorkStealingPool:创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行。

5.线程池中的参数

  • corePoolSize(线程池基本大小):当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)

  • maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。

  • keepAliveTime(线程存活保持时间)当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。

  • workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。

  • threadFactory(线程工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。

  • handler(线程饱和策略):当线程池和队列都满了,再加入线程会执行此策略。

6.任务执行流程

1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。

2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行

3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务

4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理

5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,释放空闲线程

6.当设置allowCoreThreadTimeOut(true)时,该参数默认false,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭

7.execute和submit的区别

  • execute(),执行一个任务,没有返回值。
  • submit(),提交一个线程任务,有返回值。

8.线程池中的工作队列

  • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。

  • LinkedBlockingQueue:是一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列

  • SynchronousQueue:是一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。

  • PriorityBlockingQueue:是一个具有优先级的无限阻塞队列。

9.线程任务的拒绝策略

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  • ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

http://www.ppmy.cn/news/2575.html

相关文章

STL_string

文章目录1、string2、string的用法2.1、创建 string 对象2.2、给 string 对象赋值2.3、从 string 对象尾部添加字符2.4、从 string 对象尾部追加字符串2.5、给 string 对象插入字符2.6、访问 string 对象的元素2.7、删除 string 对象的元素2.8、返回 string 对象的长度2.9、替换…

计算两个颜色相似度

1.计算两个颜色相似度的公式如下: 颜色QColor1(R1, G1, B1)转成h1,s1,v1 颜色QColor2(R2, G2, B2)转成h2,s2,v2 detah=h1-h2 detas=s1-s2 detav=v1-v2 len = qsrt(detah * detah + detas * detas + detav * detav) if (len > 1) len = 1.0 similarity = (1.0 - le…

m基于FPGA的数字下变频verilog设计

目录 1.算法描述 2.仿真效果预览 3.verilog核心程序 4.完整FPGA 1.算法描述 整个数字下变频的基本结构如下所示 NCO使用CORDIC算法&#xff0c;CIC采用h结构的CIC滤波器&#xff0c;HBF采用复用结构的半带滤波器&#xff0c;而FIR则采用DA算法结构。 这里&#xff0c;我们…

Matlab 中 global 函数实例解析

目录 global 函数 案例分析 1 案例分析 2 使用golbal的优点 1. 传递大数据的参数 2. 过多的常量需要传递 global 函数 比如在主函数里面&#xff0c;你需要设置 Nc 这个变量是一个全局变量&#xff0c;就需要声明一下&#xff1a; global Nc; 然后在子函数里面你又用到了…

蓝桥杯比赛 NOC竞赛C++ 类、函数和指针,选择题真题和模拟题汇总答案解析

第二部分 C 类、函数和指针 1、有关类的说法不正确的是 D A&#xff09;类是一种用户自定义的数据类型. B&#xff09;只有类中的成员函数才能存取类中的私有成员. C&#xff09;在类中&#xff0c;如果不做特别说明&#xff0c;所指的数据均为私有类型. D&#…

【linux】ssh免密登录

概要 服务器免密登录实际上是基于公钥的认证&#xff0c;比如希望A服务器可以免密访问B服务器&#xff0c;则需要进行如下步骤 A服务器生成密钥对将A服务器生成的公钥分发到B服务器&#xff08;写入~/.ssh/authorized_keys&#xff09;A服务器即可免密登录B服务器 生成密钥对…

Vue学习:Vue中的数据代理

<!-- 准备容器 --><div idroot> <h2>学校名称&#xff1a;{{name}}</h2><h2>学校地址&#xff1a;{{adress}}</h2></div><script>const vm new Vue({ el: #root,data: {name:Jhon,adress:street 10},});</script> vm上…

计算机的硬件系统和软件系统的关系

计算机的硬件系统和软件系统的关系是缺一不可。 硬件它是所有软件运行的物质基础。 与硬件直接接触的是操作系统&#xff0c;它处在硬件和其他软件之间&#xff0c;表示它向下控制硬件&#xff0c;向上支持其他软件。 在操作系统之外的各层分别是各种语言处理程序、数据库管理…