java的基础工作

java的基础工作酣春将尽 金招时节 纵观下去年程序员的就业形式 想来总有兄弟因为各种原因出来观望一下 由此趁着 moyu 之机 偷偷修炼一波 同时对以往工作经验 开发知识进行整理 以正我道 从 Java 工作比较重要的基础 开始 到 jvm 虚拟机 spring 框架 sql 调优 sevlet 计算机网络 常用设计模式

大家好,我是讯享网,很高兴认识大家。



酣春将尽,金招时节。纵观下去年程序员的就业形式,想来总有兄弟因为各种原因出来观望一下。由此趁着moyu之机,偷偷修炼一波,同时对以往工作经验,开发知识进行整理,以正我道。从Java工作比较重要的基础开始,到jvm虚拟机spring框架sql调优sevlet计算机网络常用设计模式tomcat服务器、和cloud微服务分布式架构消息中间件趋势下的web前端jQueryvue进行总结回顾,寻到一份心仪的工作,创建一个有单休的世界!

(一)线程与进程                          

1.深藏的线程

复习线程相关的,线程的使用出现java应用的哪些环节呢?从一个客户端请求到达服务端,从底层网卡开始,到tomcat担当起web应用的联络官,其中巧妙配合内部线程将请求转发到web应用。同时回送web应用的响应资源。

  • 主线程(Main Thread):Tomcat启动时创建的主线程,负责监听端口并接受客户端的连接请求。
  • 接收器线程(Acceptor Thread):主线程接受到客户端连接请求后,会将连接分配给接收器线程进行处理。接收器线程负责接收客户端的请求,并将请求分发给工作线程进行处理。
  • 工作线程(Worker Thread):工作线程是Tomcat中的核心线程,负责处理客户端的请求。每个工作线程都会从连接池中获取一个连接,并处理该连接上的请求。工作线程处理完请求后,将响应返回给客户端,并将连接归还给连接池。
  • 后台线程(Background Thread):后台线程用于执行一些后台任务,例如定时任务、清理任务等。这些任务通常不涉及与客户端的交互。
  • 监控线程(Monitor Thread):监控线程用于监控Tomcat的运行状态,例如检查连接池的健康状况、统计请求处理情况等。
  • 异步处理线程(Async Thread):当客户端发起异步请求时,Tomcat会创建一个异步处理线程来处理该请求。异步处理线程可以在后台执行一些长时间的操作,而不会阻塞其他请求的处理。

这么多种按职责划分的线程,在一次请求中Tomcat线程如何分配的呢?


2.系统中线程

进程(Process)是资源分配的基本单位,线程(Thread)是CPU调度的基本单位。在计算机操作系统中我们可以了解到一个进行和线程的存储结构,也同时回顾下。

  • 线程将进程的资源分和CPU调度分离开来。 以前进程既是资源分配又是CPU调度的基本单位,后来为了更好的利用高性能的CPU,将资源分配和CPU调度分开。因此,出现了线程。java的基础工作
  • 进程和线程的联系: 一个线程只能属于一个进程,一个进程可以拥有多个线程。线程之间共享进程资源。

 3.应用中线程

 值得注意的是Thread类实现的Runnable接口,本身只包含一个可由Thread调用的run方法。在我的理解中,凡是没有继承类似Thread类的类是没有异步的能力的。而只包含一个run方法体,便称为Task任务。所以需要将线程和任务分清。

这里跳过异步的方式,重点关注下可返回Task的运行原理线程池调度

Callable的运行机制

其中FutureTask继承一个特别的接口Future,Future包含一个特别的方法get()将结果赋予公共资源outcome对象。其中的等待唤醒机制使得Callable具有了能获取子线程计算结果的能力。

通过FutureTask的包装的继承Runnable的run()的特别机制,从而依次从等待队列取出子线程执行,利用子线程唤醒正阻塞的主线程。一次神奇的父子线程等待交互的动作便完成了。

 
讯享网 

线程的包工厂

 为什么要使用线程池,甚至是单线程的线程,可能接触使用较少有些疑问?其中简单的说,管控,高效,拓展。通过管理整个线程的生命周期,使得线程资源更能合理利用,便能提高效率。从而新增出定时、定期、数控等功能。

线程池从诞生到死亡,中间会经历RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五个生命周期状态。

  • RUNNING 表示线程池处于运行状态,能够接受新提交的任务且能对已添加的任务进行处理。RUNNING状态是线程池的初始化状态,线程池一旦被创建就处于RUNNING状态。
  • SHUTDOWN 线程处于关闭状态,不接受新任务,但可以处理已添加的任务。RUNNING状态的线程池调用shutdown后会进入SHUTDOWN状态。
  • STOP 线程池处于停止状态,不接收任务,不处理已添加的任务,且会中断正在执行任务的线程。RUNNING状态的线程池调用了shutdownNow后会进入STOP状态。
  • TIDYING 当所有任务已终止,且任务数量为0时,线程池会进入TIDYING。当线程池处于SHUTDOWN状态时,阻塞队列中的任务被执行完了,且线程池中没有正在执行的任务了,状态会由SHUTDOWN变为TIDYING。当线程处于STOP状态时,线程池中没有正在执行的任务时则会由STOP变为TIDYING。
  • TERMINATED 线程终止状态。处于TIDYING状态的线程执行terminated()后进入TERMINATED状态。

 (二)集合框架                     

集合框架是每个开发语言中必不可少的一环。其原因就在于数据结构是整个应用程序重中之重。当中的层次只做简单回顾,重点来了解下list、map的内在实现的优汰在于其存储结构。

1. List列表

List是Java中常用的数据结构之一,它是一个有序的集合,可以存储重复的元素。在Java中,List接口是Collection接口的子接口,它定义了一系列操作列表的方法。

讯享网

List接口有多个实现类,常用的有ArrayList和LinkedList。ArrayList是基于数组实现的,支持快速随机访问,但插入和删除元素的效率较低;LinkedList是基于链表实现的,插入和删除元素的效率较高,但随机访问的效率较低。 


2.map映射

关于其扩容机制,在首次调用put方法的时候,初始化数组table。

当HashMap中的元素个数超过数组大小(数组长度)*loadFactor(负载因子)时,就会进行数组扩容,loadFactor的默认值(DEFAULT_LOAD_FACTOR)是0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中的元素个数超过16×0.75=12(这个值就是阈值或者边界值threshold值)的时候,就把数组的大小扩展为2×16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预知元素的个数能够有效的提高HashMap的性能。

当HashMap中的其中一个链表的对象个数如果达到了8个,此时如果数组长度没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链表会变成红黑树,节点类型由Node变成TreeNode类型。当然,如果映射关系被移除后,下次执行resize方法时判断树的节点个数低于6,也会再把树转换为链表。

(三)线程安全               

线程安全是指多个线程同时访问某个资源时,不会出现不可预期的结果或者破坏数据的情况。而锁机制是一种常用的线程同步机制,用于保护共享资源的访问,确保在同一时间只有一个线程可以访问该资源。

在多线程环境下,线程安全的实现通常需要考虑以下几个方面:

  • 互斥访问:通过使用互斥锁(Mutex)或者信号量(Semaphore)等机制,确保同一时间只有一个线程可以访问共享资源。当一个线程获得了锁之后,其他线程需要等待锁的释放才能继续执行。
  • 原子操作:原子操作是指不可中断的操作,要么全部执行成功,要么全部不执行。在多线程环境下,原子操作可以通过使用原子变量或者原子操作函数来实现,确保对共享资源的操作是原子的,不会被其他线程中断。
  • 同步机制:除了锁机制外,还可以使用其他同步机制来实现线程安全,例如条件变量(Condition Variable)、读写锁(Read-Write Lock)等。这些机制可以根据具体的需求来选择合适的同步方式。

需要注意的是,使用锁机制虽然可以保证线程安全,但过多地使用锁可能会导致性能下降。因此,在设计多线程程序时,需要权衡线程安全和性能之间的平衡。

1.乐观锁

乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新时会判断此期间数据是否被更新
采取在写时先读出当前版本号,然后加锁操作比较跟上一次的版本号,如果一样则更新,如果失败则要重复读-比较-写的操作
java 中的乐观锁基本通过 CAS 操作实现的,CAS 是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败

2.悲观锁

悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁
Java 中的悲观锁就是Synchronized,AQS 框架下的锁则是先尝试 cas 乐观锁去获取锁,获取不到,才会转换为悲观锁,如 RetreenLock。

3.自旋锁
4.同步锁

关键字,用于解决多个线程间访问资源同步性问题,保证其修饰的方法或代码块任意时刻只能有一个线程访问synchronized 它可以把任非 NULL 的对象当作锁。他属于独占式悲观锁,同时属于可重入锁。

5.可重入锁

ReentantLock 继承接口Lock并实现了接口中定义的方法,他是一种可重入锁,除了能完成 synchronized 所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。

(四)网络架构            

为了解决大型互联网公司面临的高并发,海量数据处理,高可靠运行等一一系列问题。互联网公司在实践中提出了很多解决方案,来达到高可用,高性能,易伸缩,易扩展的架构目标,这些解决方案又被更多的网站重复使用,从而逐渐形成大型网站架构模式。

1.IO多路复用

进程通过告诉多路复用器(内核)(也就是select函数和poll函数)所有的socket号,多路复用器再去获取每一个socket的状态,当程序获取到某个socket号有事件发生了,则去该socket号上进行处理对应的事件,read事件或者是recived事件。(补充select函数与poll函数的区别是,前者底层是数组,所以有最大连接数的限制,后者是链表,无最大连接数的限制)

select 机制
1️⃣基本原理:
客户端操作服务器时就会产生这三种文件描述符(简称fd):writefds(写)、readfds(读)、和 exceptfds(异常)。select 会阻塞住监视 3 类文件描述符,等有数据、可读、可写、出异常或超时就会返回;返回后通过遍历 fdset 整个数组来找到就绪的描述符 fd,然后进行对应的 IO 操作。

2️⃣优点:
几乎在所有的平台上支持,跨平台支持性好

3️⃣缺点:

由于是采用轮询方式全盘扫描,会随着文件描述符 FD 数量增多而性能下降。
每次调用 select(),都需要把 fd 集合从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用户空间)。
单个进程打开的 FD 是有限制(通过FD_SETSIZE设置)的,默认是 1024 个,可修改宏定义,但是效率仍然慢。

epoll 机制
1️⃣基本原理:
没有 fd 个数限制,用户态拷贝到内核态只需要一次,使用时间通知机制来触发。通过 epoll_ctl 注册 fd,一旦 fd 就绪就会通过 callback 回调机制来激活对应 fd,进行相关的 io 操作。epoll 之所以高性能是得益于它的三个函数:

epoll_create() 系统启动时,在 Linux 内核里面申请一个B+树结构文件系统,返回 epoll 对象,也是一个 fd。
epoll_ctl() 每新建一个连接,都通过该函数操作 epoll 对象,在这个对象里面修改添加删除对应的链接 fd,绑定一个 callback 函数
epoll_wait() 轮训所有的 callback 集合,并完成对应的 IO 操作
2️⃣优点:
没 fd 这个限制,所支持的 FD 上限是操作系统的最大文件句柄数,1G 内存大概支持 10 万个句柄。效率提高,使用回调通知而不是轮询的方式,不会随着 FD 数目的增加效率下降。内核和用户空间 mmap 同一块内存实现(mmap 是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间)

4️⃣epoll 应用:redis、nginx


2.reactor模型

基础网络通信就是这种,主线程中开启监听后,只能在这里等着处理和客户端之间保持这种一读一写的连接。再后来我们学习了IO多路复用技术,也就是select、epoll、poll技术,我们可以同时处理多个连接,但是这种模式每一个客户端都要维持一个与服务器的连接,那么对于服务端如果有很多的IO连接,那么绝对是一个巨大的开销。

在制定线程策略的时候,只能根据CPU的数目来限定可用线程资源,不能根据连接并发数目来制定,也就是连接有限制。否则很难保证对客户端请求的高效和公平。多线程之间的上下文切换,造成线程使用效率并不高,并且不易扩展状态数据以及其他需要保持一致的数据,需要采用并发同步控制.

单Reactor单线程模型

也就是说一个单线程就能够实现高并发,是不是很牛逼。Reactor的核心思想就是它有一个反应堆,这个反应堆可以继续往里面加东西,然后主线程就是不断从反应堆里面取出已经反应的东西,处理它们。

单reactor多线程模型

一句话总结就是,一个反应堆,负责处理反应,把反应封装成任务交给线程池去处理读写,这样的效率适合一些每一个连接业务比较复杂的情况,把处理连接的耗时操作在分出来,提升反应堆的处理速度。

多reactor多线程模式

第三种模型比起第二种模型,是将Reactor分成两部分,mainReactor负责监听server socket,用来处理新连接的建立,将建立的socketChannel指定注册给subReactor。subReactor维护自己的selector, 基于mainReactor 注册的socketChannel多路分离IO读写事件,读写网 络数据,对业务处理的功能,另其扔给worker线程池来完成。

用户态:处于用户态的 CPU 只能受限的访问内存,并且不允许访问外围设备,⽤户态下的 CPU 不允许独占,也就是说CPU 能够被其他程序获取。

小讯
上一篇 2024-12-29 23:20
下一篇 2024-12-26 22:27

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/4862.html