Java知识总结

news2025/4/8 8:39:16

https://www.bilibili.com/video/BV1ys4y1S7Lc

1、Java中线程的实现方式

为什么说本质上只有一种实现线程的方式?实现 Runnable 接口究竟比继承 Thread 类实现线程好在哪里?

  1. 实现 Runnable 接口

public class RunnableThread implements Runnable {

    @Override

    public void run() {

        System.out.println('用实现Runnable接口实现线程');

    }

}

第 1 种方式是通过实现 Runnable 接口实现多线程,如代码所示,首先通过 RunnableThread 类实现 Runnable 接口,然后重写 run() 方法,之后只需要把这个实现了 run() 方法的实例传到 Thread 类中就可以实现多线程。

  1. 继承 Thread 类

public class ExtendsThread extends Thread {

    @Override

    public void run() {

        System.out.println('用Thread类实现线程');

    }

}

第 2 种方式是继承 Thread 类,如代码所示,与第 1 种方式不同的是它没有实现接口,而是继承 Thread 类,并重写了其中的 run() 方法。相信上面这两种方式你一定非常熟悉,并且经常在工作中使用它们。

  1. 线程池创建线程

那么为什么说还有第 3 种或第 4 种方式呢?我们先来看看第 3 种方式:通过线程池创建线程。线程池确实实现了多线程,比如我们给线程池的线程数量设置成 10,那么就会有 10 个子线程来为我们工作,接下来,我们深入解析线程池中的源码,来看看线程池是怎么实现线程的?

static class DefaultThreadFactory implements ThreadFactory {

    DefaultThreadFactory() {

        SecurityManager s = System.getSecurityManager();

        group = (s != null) ? s.getThreadGroup() :

            Thread.currentThread().getThreadGroup();

        namePrefix = "pool-" +

            poolNumber.getAndIncrement() +"-thread-";

    }

    public Thread newThread(Runnable r) {

        Thread t = new Thread(group, r,

                 namePrefix + threadNumber.getAndIncrement(),0);

        if (t.isDaemon())

            t.setDaemon(false);

        if (t.getPriority() != Thread.NORM_PRIORITY)

            t.setPriority(Thread.NORM_PRIORITY);

        return t;

    }

}

对于线程池而言,本质上是通过线程工厂创建线程的,默认采用 DefaultThreadFactory ,它会给线程池创建的线程设置一些默认值,比如:线程的名字、是否是守护线程,以及线程的优先级等。但是无论怎么设置这些属性,最终它还是通过 new Thread() 创建线程的 ,只不过这里的构造函数传入的参数要多一些,由此可以看出通过线程池创建线程并没有脱离最开始的那两种基本的创建方式,因为本质上还是通过 new Thread() 实现的。

  1. 有返回值的 Callable 创建线程

class CallableTask implements Callable<Integer> {

    @Override

    public Integer call() throws Exception {

      return new Random().nextInt();

    }

}

//创建线程池

ExecutorService service = Executors.newFixedThreadPool(10);

//提交任务,并用 Future提交返回结果

Future<Integer> future = service.submit(new CallableTask());

第 4 种线程创建方式是通过有返回值的 Callable 创建线程,Runnable 创建线程是无返回值的,而 Callable 和与之相关的 Future、FutureTask,它们可以把线程执行的结果作为返回值返回,如代码所示,实现了 Callable 接口,并且给它的泛型设置成 Integer,然后它会返回一个随机数。

但是,无论是 Callable 还是 FutureTask,它们首先和 Runnable 一样,都是一个任务,是需要被执行的,而不是说它们本身就是线程。它们可以放到线程池中执行,如代码所示, submit() 方法把任务放到线程池中,并由线程池创建线程,不管用什么方法,最终都是靠线程来执行的,而子线程的创建方式仍脱离不了最开始讲的两种基本方式,也就是实现 Runnable 接口和继承 Thread 类。

实现 Runnable 接口比继承 Thread 类实现线程要好

下面我们来对刚才说的两种实现线程内容的方式进行对比,也就是为什么说实现 Runnable 接口比继承 Thread 类实现线程要好?好在哪里呢?

首先,我们从代码的架构考虑,实际上,Runnable 里只有一个 run() 方法,它定义了需要执行的内容,在这种情况下,实现了 Runnable 与 Thread 类的解耦,Thread 类负责线程启动和属性设置等内容,权责分明。

第二点就是在某些情况下可以提高性能,使用继承 Thread 类方式,每次执行一次任务,都需要新建一个独立的线程,执行完任务后线程走到生命周期的尽头被销毁,如果还想执行这个任务,就必须再新建一个继承了 Thread 类的类,如果此时执行的内容比较少,比如只是在 run() 方法里简单打印一行文字,那么它所带来的开销并不大,相比于整个线程从开始创建到执行完毕被销毁,这一系列的操作比 run() 方法打印文字本身带来的开销要大得多,相当于捡了芝麻丢了西瓜,得不偿失。如果我们使用实现 Runnable 接口的方式,就可以把任务直接传入线程池,使用一些固定的线程来完成任务,不需要每次新建销毁线程,大大降低了性能开销。

第三点好处在于 Java 语言不支持双继承,如果我们的类一旦继承了 Thread 类,那么它后续就没有办法再继承其他的类,这样一来,如果未来这个类需要继承其他类实现一些功能上的拓展,它就没有办法做到了,相当于限制了代码未来的可拓展性。

综上所述,我们应该优先选择通过实现 Runnable 接口的方式来创建线程。

  1. Java中线程的状态

在java中,线程的状态六种,对应着枚举类型 Thread.State 的六个枚举常量:NEW 、BLOCKED、RUNNABLE、WAITING 、TIMED_WAITING、TERMINATED

1. NEW : 新建状态,至今尚未启动的线程的状态。

2. BLOCKED: 阻塞状态,受阻塞并且正在等待监视器锁的某一线程的线程状态。

3. RUNNABLE: 可运行线程的线程状态。这里其实合并了两种状态(RUNNING、RUNABLE)

4. WAITING : 等待状态,表示线程进入状态。进入此状态后,会无限等待,直到其他线程做出一些特定的动作(唤醒通知、中断通知)才会再次运行。

5. TIMED_WAITING : 计时等待状态,此状态与 WAITING 状态有些类似,但它是有时间限制的,即只会等待一段指定的时间,当时间到来前,没有被唤醒或或中断,那么时间到来了,就自动"醒来",进入RUNNABLE状态。

6. TERMINATED : 终止状态,已终止线程的线程状态。

线程在自身的生命周期中,并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换,java线程状态变迁如下图所示:

注意以下几点:

1、当线程的run方法结束时,该线程就完成。即线程死亡。但注意,此时线程的状态是死亡了,而且是不可以复活的,但是死亡的线程的对象并没有立即消失(因为Thread是一个类,是一个记录、操作线程的类),特别是在别处被引用下,你可以继续调用这个Thread实例上的大部分方法,而对线程操作的方法基本上都会抛异常,如:start()、wait()、notify()不可以再调用;

2、只要线程启动了,也就是调用start()方法,也就永远就不能再次启动;

3、线程执行的顺序与线程启动的顺序无关,start()线程启动,线程首先由新建状态变成就绪状态,并不是直接就是运行状态,即不会马上就运行,何时进入CPU运行,得看调度算法;

4、java 将操作系统当中的就绪 与 运行两个状态合并为运行状态。

5、线程进入synchronize修饰的方法或代码块中,线程的状态变为阻塞状态。但如果线程进入的是Lock接口的代码块中,却是等待状态。这是因为Lock对于阻塞的本质实现是使用了LockSupport类中的相关方法。

6、WAITING 、TIMED_WAITING 两种等待状态都是可以被"中断"打断的,所以那些将线程变为等待状态的方法,如wait()、sleep等都要 捕获 InterruptedException异常。

3、Java中如何停止线程

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

在 run() 方法执行完毕后,该线程就终止了。但是在某些特殊的情况下,run() 方法会被一直执行;比如在服务端程序中可能会使用 while(true) { … } 这样的循环结构来不断的接收来自客户端的请求。此时就可以用修改标志位的方式来结束 run() 方法。
2)使用 stop() 方法强行终止线程,但是不推荐使用这个方法,该方法已被弃用。

通过查看 JDK 的 API,我们会看到 java.lang.Thread 类型提供了一系列的方法如 start()、stop()、resume()、suspend()、destory()等方法来管理线程。但是除了 start() 之外,其它几个方法都被声名为已过时(deprecated)。

虽然 stop() 方法确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且该方法已被弃用,最好不要使用它。

为什么弃用stop:

调用 stop() 方法会立刻停止 run() 方法中剩余的全部工作,包括在 catch 或 finally 语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。

调用 stop() 方法会立即释放该线程所持有的所有的锁,导致数据得不到同步,出现数据不一致的问题。
3)使用 interrupt 方法中断线程。

现在我们知道了使用 stop() 方式停止线程是非常不安全的方式,那么我们应该使用什么方法来停止线程呢?答案就是使用 interrupt() 方法来中断线程。

需要明确的一点的是:interrupt() 方法并不像在 for 循环语句中使用 break 语句那样干脆,马上就停止循环。调用 interrupt() 方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程。

也就是说,线程中断并不会立即终止线程,而是通知目标线程,有人希望你终止。至于目标线程收到通知后会如何处理,则完全由目标线程自行决定。这一点很重要,如果中断后,线程立即无条件退出,那么我们又会遇到 stop() 方法的老问题。

事实上,如果一个线程不能被 interrupt,那么 stop 方法也不会起作用。

public boolean Thread.isInterrupted() //判断是否被中断

public static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态

这两个方法使得当前线程能够感知到是否被中断了(通过检查标志位)。

4、sleep和wait的方法区别

5、并发编程的三大特性

6、什么是CAS?有什么优缺点?

7、@Contended注解有什么用?

8、Java中的四种引用类型 强 软 弱 虚

9、threadlocal的内存泄漏问题?

10、Java中锁的分类

11、synchronized在jdk1.6的优化?

12、synchronized的实现原理?

13、什么是AQS?

14、AQS唤醒节点时,为何从后往前找?

15、reentrantLock和synchronized的区别?

16、reentrantReadWriteLock的实现原理?

17、jdk中提供了哪些线程池?

18、线程池的核心参数有什么?

19、线程池的状态?

20、线程池的执行流程?

21、线程池添加工作线程的流程?

22、线程池为何要构建空任务的非核心线程?

23、线程池使用完为何必须shutdown?

24、线程池的核心参数到底如何设置?

25、ConcurrentHashMap的散列算法?

26、ConcurrentHashMap初始化数组的流程?

27、ConcurrentHashMap扩容的流程?

28、ConcurrentHashMap读取数据的流程?

29、ConcurrentHashMap中计数器的实现

30、谈谈对spring的理解

31、spring中应用到的设计模式有哪些?

单利模式、原型模式、模板模式

32、谈谈Autowired和Resource两个注解的区别?

33、谈谈spring常用的注解

34、谈谈对循环依赖的理解

35、spring是如何解决循环依赖的问题的

36、spring是解决构造注入的循环依赖问题的

37、spring的循环依赖为什么需要三级缓存?

38、spring中bean对象的生命周期

39、spring中支持的作用域有几种?

40、spring中事务的隔离级别介绍

41、spring中事务的隔离级别

42、spring中事务的本质

43、谈谈beanfactory和applicationContext的理解

44、谈谈beanfactorypostprocessor的理解

45、谈谈beanpostprocessor的理解

46、谈谈对springmvc框架的理解,spring和springmvc的关系?

47、谈谈delegatingFilterProxy的理解

48、谈谈springboot自动装配原理的理解

49、谈谈对import注解的理解

50、谈谈对deferredimportselector的理解

51、谈谈springboot中的bootstrap.yml文件的作用

52、谈谈indexed注解的作用

53、如何对属性文件中的账户密码进行加密?

54、@Componet。@Controller。@Repository。@Service注解的区别

55、有哪些通知类型(advice)

56、介绍下spring中的依赖注入

57、Spring中的bean单利对象是否线程安全?

58、Redis为什么快?

59、Redis适合的应用场景

  1. Redis6.0之前为什么一直不使用多线程
  2. Redis6.0为什么要引入多线程
  3. Redis有哪些高级功能?
  4. 怎么理解redis中的事务?
  5. Redis过期策略以及内存淘汰机制?
  6. 什么事缓存穿透?如何避免?
  7. 什么事缓存雪崩?如何避免?
  8. 使用redis如何设计分布式锁?
  9. 怎么使用redis实现消息队列?
  10. 什么事bigkey?会有什么影响?
  11. Redis如何解决key冲突?
  12. 怎么提高缓存命中率?
  13. Redis持久化方式有哪些?有什么区别?
  14. Redis为什么需要把所有数据放到内存中?
  15. 如何保证缓存与数据库双写时的数据一致性
  16. Redis集群方案应该怎么做?
  17. Redis集群方案什么情况下会导致整个集群不可用?
  18. 说一说redis哈希槽的概念?
  19. Redis集群会有写操作丢失吗?为什么?
  20. Redis常见性能问题和解决方案有哪些?
  21. Redis热数据和冷数据是什么?
  22. 什么情况下可能导致redis阻塞?
  23. Redis过期策略有哪些?LRU算法你了解吗?
  24. Jvm中的类加载问题
  25. 谈下你对类加载器的理解
  26. 双亲委派机制
  27. 运行时数据区
  28. 栈帧结构与动态链接
  29. 为什么java堆要进行分代设计
  30. 老年代的担保机制
  31. 对象的创建过程
  32. 持久代与元空间以及方法区的关系
  33. CMS垃圾收集器
  34. G1垃圾收集器

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ai智能改写文案-ai同义转换

文案创作是现代广告营销中不可或缺的一环&#xff0c;一个好的文案不仅可以提升产品的购买率&#xff0c;还可以实现品牌等方面的推广。但是&#xff0c;文案的创作需要耗费大量的时间和精力&#xff0c;如果能够利用智能化技术进行改写&#xff0c;不仅可以大大缩短文案创作时…

JAVA内存不足导致频繁回收和swap引起的性能问题 故障重现(内存篇2)

背景起因&#xff1a; 记起以前的另一次也是关于内存的调优分享下 有个系统平时运行非常稳定运行&#xff08;没经历过大并发考验&#xff09;&#xff0c;然而在一次活动后&#xff0c;人数并发一上来后&#xff0c;系统开始卡。 我按经验开始调优&#xff0c;在每个关键步骤…

本地安装directus

简介 Directus 是用于管理 SQL 数据库内容的实时 API 和 App 控制面板。 API会根据数据库模式/内容的实时更改动态更新&#xff08;无需重新启动服务器&#xff09;。 Directus安装在任何新的或现有的 SQL 数据库之上&#xff0c;提供 API 层&#xff08;REST、GraphQL、JS-SD…

获取商品SKU信息API调用代码展示、请求参数和返回值说明

SKU是什么意思 最小存货单位&#xff08;SKU&#xff09;&#xff0c;全称为stock keeping unit&#xff0c;即库存进出计量的基本单元&#xff0c;可以是以件、盒、托盘等为单位。SKU这是对于大型连锁超市DC&#xff08;配送中心&#xff09;物流管理的一个必要的方法。现在已…

MySQL数据库从入门到精通学习第2天(创建数据库)

创建数据库 通过CREATE DATABASE语句来创建数据库通过CREATE SCHEMA语句来创建数据库通过IF NOT EXISTS进行判断创建 通过CREATE DATABASE语句来创建数据库 创建数据库的语法格式如下&#xff1a; CREATE DATABASE 【数据库名】; 创建数据库的库名跟标识符一样也是有要求的&…

实际项目集成分布式一致性协议 Raft

实际项目集成分布式一致性协议 Raft 文章目录 实际项目集成分布式一致性协议 Raft前言1.raft 是什么&#xff1f;2.SOFAJRaft2.1 功能特性 3.Nacos 分布式一致性设计3.1 nacos 分布式协议架构设计3.1 nacos 用 jraft 做什么3.2 Distro 协议 4.实际项目-Spring 工程4.1 旧版项目…

SA168 3BSE003389R1

SA168 3BSE003389R1 远程终端控制系统&#xff08;RTU&#xff09;可连接到其他设备。RTU可将设备上的电气信号转换为数字的值&#xff0c;例如一个开关或阀开/关的状态&#xff0c;或是仪器量测到的压力、流量、电压或电流。也可以借由信号转换及传送信号来控制设备&#xff0…

硬盘分区怎么分?新手该如何操作?

相信很多电脑用户都遇到过硬盘分区的情况。刚拿到手的新电脑&#xff0c;基本上都是一个或两个磁盘分区&#xff0c;这不满足我们的使用习惯&#xff0c;比如我们在不同的分区存放不同的东西&#xff0c;只有一个分区就很难做到&#xff0c;所以这时候需要进行磁盘分区。那么硬…

解读“SAP集成架构咨询方法论”

如果你是SAP ERP相关工作的&#xff0c;建议大家点开原文地址去看&#xff0c;会学习到其他很多与这个行业更多的资料。 原文地址&#xff1a;解读“SAP集成架构咨询方法论” | SAP Blogs 原文地址&#xff1a;解读“SAP集成架构咨询方法论” | SAP Blogs ——————————…

经验分享:如何有效应对Facebook广告数据波动问题?

Facebook广告作为一种重要的数字营销工具&#xff0c;可以帮助企业和品牌快速获得目标受众的关注和转化。然而&#xff0c;由于广告投放过程的不稳定性&#xff0c;Facebook广告数据波动问题也经常出现。 对于广告主而言&#xff0c;如何应对Facebook广告数据波动问题&#xf…

【JVM】JMM

一、JMM JVM 内存模型是用来屏蔽掉各种硬件和操作系统的内存访问差异&#xff0c;以实现让 Java 程序在各个平台下都能达到一致的内存访问效果。JVM 内存模型规定了所有的共享变量都是存储在主内存&#xff0c;每个线程还有自己的工作内存&#xff0c;线程的工作内存保存了该线…

【教学类-34-01】拼图(运动项目)3*4格子(中班主题《个别化拼图》健康偏艺术-美术)

背景需求&#xff1a; 一个月的Python纸类学具研究中&#xff0c;我发现个别男孩喜欢把作业中的数字、图案、单元格剪成小块&#xff08;小卡片&#xff09;进几周&#xff0c;剪条、剪块的孩子人数也慢慢递增。 幼儿需求&#xff1a;锻炼手指精细动作的需求、或者获得更多物…

Idea+maven+spring-cloud项目搭建系列--13 整合MyBatis-Plus多数据源dynamic-datasource

前言&#xff1a;对于同一个系统&#xff0c;不同的租户需要自己独立分隔的数据库&#xff08;每个数据库的表结构可以是相同的&#xff09;&#xff0c;同时也要支持跨数据源的查询&#xff1b;并且支持分布式事务&#xff0c;如果这里不使用分库分表插件&#xff0c;需要怎样…

python-day4(字符串、列表、生成式和生成器、使用元组、集合、字典)

字符串和常用数据结构 简单用法 所谓字符串&#xff0c;就是由零个或多个字符组成的有限序列&#xff0c;一半记为sa1a2a3…an(0<n<∞)。在python中&#xff0c;如果我们把单个或多个字符用单引号或者双引号包围起来&#xff0c;就可以表示一个字符串。 s1 hello, wo…

【生物信息】用隐马尔可夫模型对生物序列进行建模

文章目录 Modeling biological sequences with HMMSParsing longer sequences. 举例子Our frst HMM: Detecting GC-rich regionsRunning the model: Probability of a sequence 维特比算法 Viterbi一个摸球例子回到课堂 求解参数 来自Manolis Kellis教授的课 教了隐马尔可夫在基…

FPGA开发之HDMI Transmitter接口设计

HDMI简介&#xff1a; High Definition Multimedia 高清多媒体接口&#xff0c;一种全数字化视频和声音发送接口&#xff0c;可以发送未压缩的音频及视频信号 物理接口&#xff1a; 电气介绍&#xff1a; TMDS&#xff08;Transition Minimized Differential Signaling&#x…

自定义类型——位段

什么是位段&#xff1f; 位段又叫做位域&#xff0c;具体是一种可以把数据以位的形式紧凑的存储&#xff0c;并允许程序员对此结构位进行操作的数据结构 当结构体的成员变量定义之后浪费了较大的空间 &#xff08;比如int a 2&#xff0c;则浪费了30个比特位的空间&#xff0…

Mail 服务器

Mail 服务器 1. 概念及协议2. 工具2.1 Postfix2.2 dovecot2.3 bind 3. 搭建3.1 DNS服务设置3.2 安装配置 postfix3.3 安装配置 dovecot 4. foxmail验证 1. 概念及协议 邮件服务器也采用的是C/S工作模式&#xff0c;通过SMTP,POP,IMAP协议来是实现邮件的发送和接收的。 SMTP 的…

Java入坑之IO操作

目录 一、IO流的概念 二、字节流 2.1InputStream的方法 2.2Outputstream的方法 2.3资源对象的关闭&#xff1a; 2.4transferTo()方法 2.5readAllBytes() 方法 2.6BufferedReader 和 InputStreamReader 2.7BufferedWriter 和 OutputStreamWriter 三、路径&#xff1a;…

Qt5 编译QtXlsx并添加为模块[Windows]

00.QtXlsx是什么&#xff1f;能干什么&#xff1f; QtXlsx是一个可以读写Excel文件的库。它不需要Microsoft Excel&#xff0c;可以在Qt5支持的任何平台上使用。 可以创建、读取、编辑.xlsx文件。 01.如何编译&#xff1f; 1.1编译环境&#xff1a; Windows10平台&#xff1b;…