java面试 - 多线程并发篇

多线程&并发篇

chatGPT以及GPT-4免费体验!

java面试 - 基础篇
java面试-JVM篇
java面试-spring篇 (持续更新中)
java面试-MyBatis篇 (持续更新中)
java面试-springBoot篇 (持续更新中)
java面试-MySql篇 (持续更新中)
java面试-Redis篇 (持续更新中)
java面试-springCloud篇 (持续更新中)
java面试-Nginx篇 (持续更新中)
java面试-zookeeper篇 (持续更新中)
java面试-kafka篇 (持续更新中)
java面试-MQ篇 (持续更新中)
java面试-Elasticsearch篇 (持续更新中)
java面试-linux篇
java面试-数据结构与算法篇 (持续更新中)

1、Java中实现多线程有几种方法

  • 继承Thread类;
  • 实现Runnable接口;
  • 实现Callable接口通过FutureTask包装器来创建Thread线程;
  • 使用ExecutorService、Callable、Future实现有返回结果的多线程(也就是使用了ExecutorService来 管理前面的三种方式)。

2、如何停止一个正在运行的线程

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

  2. 使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的 方法。

  3. 使用interrupt方法中断线程。

    class MyThread extends Thread {volatile boolean stop = false;public void run() {while (!stop) {System.out.println(getName() + " is running");try {sleep(1000);} catch (InterruptedException e) {System.out.println("week up from blcok...");stop = true; // 在异常处理代码中修改共享变量的状态}}System.out.println(getName() + " is exiting...");}
    }
    class InterruptThreadDemo3 {public static void main(String[] args) throws InterruptedException {MyThread m1 = new MyThread();System.out.println("Starting thread...");m1.start();Thread.sleep(3000);System.out.println("Interrupt thread...: " + m1.getName());m1.stop = true; // 设置共享变量为truem1.interrupt(); // 阻塞时退出阻塞状态Thread.sleep(3000); // 主线程休眠3秒以便观察线程m1的中断情况System.out.println("Stopping application...");}
    }
    

3、notify()和notifyAll()有什么区别?

notify可能会导致死锁,而notifyAll则不会

任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行synchronized 中的代码 使用notifyall,可以唤醒

所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。

wait() 应配合while循环使用,不应使用if,务必在wait()调用前后都检查条件,如果不满足,必须调用 notify()唤醒另外的线程来处理,自己继续wait()直至条件满足再往下执行。

notify() 是对notifyAll()的一个优化,但它有很精确的应用场景,并且要求正确使用。不然可能导致死 锁。正确的场景应该是 WaitSet中等待的是相同的条件,唤醒任一个都能正确处理接下来的事项,如果 唤醒的线程无法正确处理,务必确保继续notify()下一个线程,并且自身需要重新回到WaitSet中.

4、sleep()和wait() 有什么区别?

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中 的。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当 指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。

当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。

5、volatile 是什么?可以保证有序性吗?

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语 义:

  1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他 线程来说是立即可见的,volatile关键字会强制将修改的值立即写入主存。

  2. 禁止进行指令重排序。

    volatile 不是原子性操作

    什么叫保证部分有序性?

    当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果 已经对后面的操作可见;在其后面的操作肯定还没有进行;

    x = 2; //语句1
    y = 0; //语句2
    flag = true; //语句3
    x = 4; //语句4
    y = -1; //语句5
    

    由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前 面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是 不作任何保证的。

    使用 Volatile 一般用于 状态标记量 和 单例模式的双检锁

6、Thread 类中的start() 和 run() 方法有什么区别?

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果 不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会 启动新线程。

7、为什么wait, notify 和 notifyAll这些方法不在thread类里面?

明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需 要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在 等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们 定义在Object类中因为锁属于对象。

8、为什么wait和notify方法要在同步块中调用?

  1. 只有在调用线程拥有某个对象的独占锁时,才能够调用该对象的wait(),notify()和notifyAll()方法。

  2. 如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。

  3. 还有一个原因是为了避免wait和notify之间产生竞态条件。

    wait()方法强制当前线程释放对象锁。这意味着在调用某对象的wait()方法之前,当前线程必须已经获得 该对象的锁。因此,线程必须在某个对象的同步方法或同步代码块中才能调用该对象的wait()方法。

    在调用对象的notify()和notifyAll()方法之前,调用线程必须已经得到该对象的锁。因此,必须在某个对 象的同步方法或同步代码块中才能调用该对象的notify()或notifyAll()方法。

    调用wait()方法的原因通常是,调用线程希望某个特殊的状态(或变量)被设置之后再继续执行。调用 notify()或notifyAll()方法的原因通常是,调用线程希望告诉其他等待中的线程:“特殊状态已经被设置”。 这个状态作为线程间通信的通道,它必须是一个可变的共享状态(或变量)。

9、Java中interrupted 和 isInterruptedd方法的区别?

interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机 制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线 程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法 isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出 InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其 它线程调用中断来改变。

10、Java中synchronized 和 ReentrantLock 有什么不同?

相似点:

这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一 个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行 线程阻塞和唤醒的代价是比较高的.

区别:

这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需 要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配 合try/finally语句块来完成。

Synchronized进过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指 令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经 拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计 算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释 放为止。

由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized, ReentrantLock类提供了一些高级功能,主要有以下3项:

  1. 等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于 Synchronized来说可以避免出现死锁的情况。
  2. 公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁, ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性 能不是很好。
  3. 锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。

11、有三个线程T1,T2,T3,如何保证顺序执行?

在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个 线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用 T2,T2调用T1),这样T1就会先完成而T3最后完成。

实际上先启动三个线程中哪一个都行, 因为在每个线程的run方法中用join方法限定了三个线程的执行顺序。

public class JoinTest2 { // 1.现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行 public static void main(String[] args) { final Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("t1"); } }); final Thread t2 = new Thread(new Runnable() {@Override public void run() {try { // 引用t1线程,等待t1线程执行完 t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2"); } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { // 引用t2线程,等待t2线程执行完 t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t3"); }});t3.start();//这里三个线程的启动顺序可以任意,大家可以试下!t2.start();t1.start();}
}

12、SynchronizedMap和ConcurrentHashMap有什么区别?

SynchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步。而 ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要有一个线程访问 map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程, 仍然可以对map执行某些操作。

所以,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优 势。同时,同步操作精确控制到桶,这样,即使在遍历map时,如果其他线程试图对map进行数据修 改,也不会抛出ConcurrentModificationException。

13、什么是线程安全

线程安全就是说多线程访问同一代码,不会产生不确定的结果。

在多线程环境中,当各线程不共享数据的时候,即都是私有(private)成员,那么一定是线程安全的。 但这种情况并不多见,在多数情况下需要共享数据,这时就需要进行适当的同步控制了。

线程安全一般都涉及到synchronized, 就是一段代码同时只能有一个线程来操作 不然中间过程可能会 产生不可预制的结果。

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运 行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

14、Thread类中的yield方法有什么作用?

Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且 只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield()的线程有可能在进入 到暂停状态后马上又被执行。

15、Java线程池中submit() 和 execute()方法有什么区别?

两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而 submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了 Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方 法。

16、说一说自己对于 synchronized 关键字的了解

synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修 饰的方法或者代码块在任意时刻只能有一个线程执行。 另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依 赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果 要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态 转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方对从 JVM 层面对synchronized 较大优 化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自 旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

17、说说自己是怎么使用 synchronized 关键字,在项目中用到了 吗synchronized关键字最主要的三种使用方式:

修饰实例方法: 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁

修饰静态方法: 也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例 对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如 果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的 静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁 是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。

修饰代码块: 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

总结: synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上 锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因 为JVM中,字符串常量池具有缓存功能!

18、什么是线程安全?

Vector是一个线程安全类吗? 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运 行结果和单线程运行的结果是一样的,而且其他的变量 的值也和预期的是一样的,就是线程安全的。一 个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可 以将集合类分 成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似 的ArrayList不是线程安全的。

19、 volatile关键字的作用?

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语 义:

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他 线程来说是立即可见的。
  • 禁止进行指令重排序。
  • volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读 取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  • volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
  • volatile仅能实现变量的修改可见性,并不能保证原子性;synchronized则可以保证变量的修改可 见性和原子性。
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

20、常用的线程池有哪些?

  • newSingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照 任务的提交顺序执行。
  • newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达 到线程池的最大大小。
  • newCachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池 大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
  • newScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任 务的需求。
  • newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务 的需求。

21、简述一下你对线程池的理解

(如果问到了这样的问题,可以展开的说一下线程池如何用、线程池的好处、线程池的启动策略)合理 利用线程池能够带来三个好处。

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系 统的稳定性,使用线程池可以进行统一的分配,调优和监控。

22、Java程序是如何执行的

我们日常的工作中都使用开发工具(IntelliJ IDEA 或 Eclipse 等)可以很方便的调试程序,或者是通过打 包工具把项目打包成 jar 包或者 war 包,放入 Tomcat 等 Web 容器中就可以正常运行了,但你有没有 想过 Java 程序内部是如何执行的?其实不论是在开发工具中运行还是在 Tomcat 中运行,Java 程序的 执行流程基本都是相同的,它的执行流程如下:

  • 先把 Java 代码编译成字节码,也就是把 .java 类型的文件编译成 .class 类型的文件。这个过程的 大致执行流程:Java 源代码 -> 词法分析器 -> 语法分析器 -> 语义分析器 -> 字符码生成器 -> 最终 生成字节码,其中任何一个节点执行失败就会造成编译失败;
  • 把 class 文件放置到 Java 虚拟机,这个虚拟机通常指的是 Oracle 官方自带的 Hotspot JVM;
  • Java 虚拟机使用类加载器(Class Loader)装载 class 文件;
  • 类加载完成之后,会进行字节码效验,字节码效验通过之后 JVM 解释器会把字节码翻译成机器码 交由操作系统执行。但不是所有代码都是解释执行的,JVM 对此做了优化,比如,以 Hotspot 虚 拟机来说,它本身提供了 JIT(Just In Time)也就是我们通常所说的动态编译器,它能够在运行时 将热点代码编译为机器码,这个时候字节码就变成了编译执行。Java 程序执行流程图如下

在这里插入图片描述

23、说一说自己对于 synchronized 关键字的了解

synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修 饰的方法或者代码块在任意时刻只能有一个线程执行。 另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依 赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果 要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态 转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方对从 JVM 层面对synchronized 较大优 化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自 旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

24、说说自己是怎么使用 synchronized 关键字,在项目中用到了 吗

synchronized关键字最主要的三种使用方式:

  • 修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
  • 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 。也就是给当前 类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份,所以对该类的所有对象都加了 锁)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个 实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前 实例对象锁。
  • 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 和 synchronized 方法一样,synchronized(this)代码块也是锁定当前对象的。synchronized 关键字 加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。这里再提一下: synchronized关键字加到非 static 静态方法上是给对象实例上锁。另外需要注意的是:尽量不要 使用 synchronized(String a) 因为JVM中,字符串常量池具有缓冲功能!

下面我已一个常见的面试题为例讲解一下 synchronized 关键字的具体使用。

面试中面试官经常会说:“单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单利 模式的原理呗!

双重校验锁实现对象单例(线程安全)

public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() { } public static Singleton getUniqueInstance() { //先判断对象是否已经实例过,没有实例化过才进入加锁代码 if (uniqueInstance == null) { //类对象加锁 synchronized (Singleton.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } 
} 

另外,需要注意 uniqueInstance 采用 volatile 关键字修饰也是很有必要。

uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段 代码其实是分为三步执行:

  1. 为 uniqueInstance 分配内存空间 2
  2. 初始化 uniqueInstance 3
  3. 将 uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出先 问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3, 此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此 时 uniqueInstance 还未被初始化。

使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

25、 讲一下 synchronized 关键字的底层原理

synchronized 关键字底层原理属于 JVM 层面。

① synchronized 同步语句块的情况

public class SynchronizedDemo { public void method() { synchronized (this) { System.out.println("synchronized 代码块"); } } 
} 

通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息:首先切换到类的对应目录 执行 javac SynchronizedDemo.java 命令生成编译后的 .class 文件,然后执行 javap -c -s -v -l SynchronizedDemo.class 。

在这里插入图片描述

从上面我们可以看出: synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置

当执行 monitorenter 指令时,线程试图获取锁也就是获取 monitor(monitor对象存在于每个Java对象 的对象头中,synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的 原因) 的持有权.当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行 monitorexit 指令后,将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等 待,直到锁被另外一个线程释放为止。

② synchronized 修饰方法的的情况

public class SynchronizedDemo2 { public synchronized void method() { System.out.println("synchronized 方法"); } 
} 

在这里插入图片描述

synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。

26、 为什么要用线程池?

线程池提供了一种限制和管理资源(包括执行一个任务)。 每个线程池还维护一些基本统计信息,例如 已完成任务的数量。

这里借用《Java并发编程的艺术》提到的来说一下使用线程池的好处:

  • 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。 当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系 统的稳定性,使用线程池可以进行统一的分配,调优和监控。

27、 实现Runnable接口和Callable接口的区别

如果想让线程池执行任务的话需要实现的Runnable接口或Callable接口。 Runnable接口或Callable接 口实现类都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。两者的区别在于 Runnable 接口不会返回结果但是 Callable 接口可以返回结果。

备注: 工具类 Executors 可以实现 Runnable 对象和 Callable 对象之间的相互转换。 ( Executors.callable(Runnable task) 或 Executors.callable(Runnable task,Object resule) )。

28、 执行execute()方法和submit()方法的区别是什么呢?

  1. execute() 方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
  2. submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future 对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前 线程直到任务完成,而使用 get(long timeout,TimeUnit unit) 方法则会阻塞当前线程一段时间 后立即返回,这时候有可能任务没有执行完。

29、 如何创建线程池

《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽 的风险*

Executors 返回线程池对象的弊端如下:

  • FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
  • CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。

方式一:通过构造方法实现

在这里插入图片描述

方式二:通过Executor 框架的工具类Executors来实现 我们可以创建三种类型的

ThreadPoolExecutor:

  • FixedThreadPool : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。 当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在 一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。

  • SingleThreadExecutor: 方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线 程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。

  • CachedThreadPool: 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数 量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新 的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复 用。

    对应Executors工具类中的方法如图所示:

  • FixedThreadPool : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。 当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在 一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。

  • SingleThreadExecutor: 方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线 程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。

  • CachedThreadPool: 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数 量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新 的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复 用。

    对应Executors工具类中的方法如图所示:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/11771.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5分钟!使用ChatGPT读懂一本书;写给独立开发者的设计指南;麦肯锡报告:生成式AI的经济潜力;GitHub报告:AI对开发影响深远 | ShowMeAI日报

👀日报&周刊合集 | 🎡生产力工具与行业应用大全 | 🧡 点赞关注评论拜托啦! 🤖 时代杂志「2023年100家最具影响力企业」 时代杂志发布了「TIME100 MOST INFLUENTIAL COMPANIES 2023」,评选出2023年最具影…

自然语言成了编程语言,不会写 Prompt 的程序员会面临失业吗?

作者 | 王启隆 出品 | CSDN(ID:CSDNnews) 如果让一个活在 18 世纪的人看看今天的世界,他会被现代科技的伟大所震慑,沉醉于未来社会的梦幻;但如果让一个 15 世纪的人去看看 18 世纪的世界,就感受…

文案策划将会被ChatGPT淘汰么?道叔用了两月后,惊呆了!

用了ChatGPT一段时间了,发现这真的是一个非常好用的划时代的产品。 我们文案策划人如果用熟练了的话,能够节省大量时间。未来,各行各业的脑力劳动者真的都不需要现在这么多了。 如果说,未来机器人将把大量工人淘汰的话&#xff…

一文看懂GPT风口,都有哪些创业机会?

新时代的淘金者,低附加价值的创业要谨慎,高附加价值、低技术门槛创业也要谨慎,主干道边上的创业也要谨慎。不少朋友看完不淡定了,干什么都谨慎,回家躺平好了,我有个朋友,靠ChatGPT,半…

虚拟邮箱地址-可随意接收邮箱验证码——网站

唠下互联网环境的嗑:国内的互联网环境在注册账号这一方面是真的不尊重个人隐私,只有手机号码注册的选项。vanker留意到海外的网址基本都是邮箱即可注册账号,希望国家能够制定详细的相应标准。所以提醒大家:在一般的网站上不要轻易…

ChatGPT 真的太强大了!

如果你问我,2023年最大的机会是什么?那一定是——ChatGPT 从2月份GPT3.5版本发布,到现在的4.0PLUS升级版,ChatGPT的迭代速度已经完全超过了我们的想象。 它已经可以替代很多行业的工作(比如说:文案、程序、…

chatGpt AI智能模拟面试系统开发

在当今竞争激烈的就业市场,求职者不仅需要具备扎实的专业知识和技能,还需要通过面试展现出自己的优势。 chatGpt AI智能模拟面试系统利用了人工智能技术,以及OpenAI的强大语言模型ChatGpt为基础。旨在为求职者提供一个真实且个性化的面试体验…

张俊林:由ChatGPT反思大语言模型(LLM)的技术精要

原文:张俊林:由ChatGPT反思大语言模型(LLM)的技术精要 张俊林 人机与认知实验室 2023-02-15 00:00 发表于北京 实话实说,国内在LLM模型相关技术方面,此刻,距离最先进技术的差距进一步加大了。…

提示工程师指南3-Prompt工程-高级提示

高阶Prompting 到这一步,应该很明显,改进提示有助于在不同任务上获得更好的结果。这就是Prompt工程背后的整个理念。 虽然之前的例子很有趣,但在我们深入了解更高级的概念之前,让我们先正式地介绍一些概念。 文章目录 高阶Promp…

Interview: Kevin Kelly, editor, author, and futurist采访:凯文·凯利,编辑、作家、未来学家

By Cmichel67 - Own work, CC BY-SA 4.0 作者:Cmichel 67-自己的作品,CC BY-SA 4. 0 Kevin Kelly is one of the thinkers who helped define the ethos of the tech industry from its early days. As an editor of the Whole Earth Catalog in the 198…

文心一言云服务下周上线/ 亚马逊再裁9000人/ 首款GPT-4医用软件问世…今日更多新鲜事在此...

日报君 发自 凹非寺量子位 | 公众号 QbitAI 大噶好,今天是3月21日星期二,打工人,勇敢冲(:з」∠) 科技圈又发生了哪些新鲜事,一起来和日报君看看~ 亚马逊再裁员9000人 北京时间3月20日晚间,亚马逊宣布将再裁…

【调研】生成式PLM模型(偏LLM)压缩

研究背景 常用的生成模型 下表总结了现在常用的生成模型的架构、参数量、尺寸和开源地址。其中参数量基本为亿万级别,以decoder的架构为主,模型尺寸在500MB以上。 模型名称架构尺寸层数参数量(Billion)开源地址备注GPT-2decoder548 MB481.5Bhttps://h…

哈哥的博客阅读指南,一文对接全链路导引 --- 未完待续~

文章目录 ⭐️ 一、关于 "易编程社区"🌟 社区及星球诞生的初衷🌟 加入社区和星球可以收获什么?🌟 来自哈哥的公开承诺🌟 哈哥的简介 ⭐️ 二、星荐官计划奖金池⭐️ 三、专栏解读🌟 专栏 - 编程初…

一句话让ChatGPT 支持图片回复!

ChatGPT 很智能,很聪明。但是它被困在了互联网里,只能通过网页上的文字,和我们交流。 就像历史上的一个个有趣的灵魂,我们只能通过书中的文字和故事,才能领会到他们的千古风流。 纯文字的方式,还是太单调…

outlook 回复邮件的邮件头使用RE而不是回复

在outlook的"工具" -> "选项" : 点击 “选项" -> "邮件格式" -> "国际选项" : 这里勾选上“常规设置”的选项即可。

科大讯飞版ChatGPT开放内测(文末附内测地址,亲测一秒通过审核)

本周讯飞骤然向开发者提供了内测通道,取名为讯飞星火认知大模型(以下亲切地叫它阿讯)对外开启内测。 国内大模型关注度陡增后,科大讯飞率先给出了deadline:5月6日上线产品。没想到,他们毫无征兆地开启了内…

体验讯飞星火认知大模型,据说中文能力超越ChatGPT

📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言&#x1f4d…

超越ChatGPT?新发布:“讯飞星火认知大模”到底行不行?

国内又一巨头发布 大语言模型,是 PPT 融资还是真材实料 ? 作为程序员,到底面对这一趋势,我们何去何从 ? 目录 讯飞星火,5月6日如约而至 一、你真的了解科大讯飞吗? 二、讯飞星火大模型将超越…

科大讯飞版ChatGPT开始内测《讯飞星火》

科大讯飞版ChatGPT产品,提前交卷了! 就在昨夜,讯飞骤然向开发者提供了内测通道,取名为讯飞星火认知大模型对外开启内测。 还有个神奇的英文名字Spark Desk,据说有“火花桌面智能助手”的意思。 申请的过程很简单。用…

科大讯飞股价迅飞 大模型逊色

5月8日,科大讯飞的股价开盘后放量涨停,报63.86元。5月6日,这家公司赶在A股周末休市前一天发布了自家的自然语言大模型“讯飞星火认知大模型”(以下简称“星火”)。 自OpenAI发布GPT-4后,国内的百度、华为、…