专栏首页Android 进阶Android 开发艺术探索笔记二

Android 开发艺术探索笔记二

理解window与windowManager

不管是Activity,Dialog还是Toast,它们视图都是附加在window上的,window才是view的直接管理者。

  1. FLAG_NOT_FOCUSABLE:表示不需要获取焦点,也不需要接收各种输入事件,同时启用FLAG_NOT_TOUCH_MODE 最终事件直接会传递给下层具有焦点的window
  2. FLAG_NOT_TOUCH_MODE ,系统会将当前window区域以外的单击事件传递给底层的window,当前区域内的单击事件自己处理,一般需要开启此标记,否则window将无法收到单击事件。
  3. FLAG_SHOW_WHEN_LOCKED:开启此模式,让window显示在锁屏界面上。

Android 8.0之前源码

Window添加过程

  1. 检查参数是否合法,如果是子window还需调整布局参数
  2. 创建viewRootImpl并将View添加到列表中
  • mViews存储所有window对应的view
  • mRoots存储所有window对应的ViewRootImpl
  • mParams存储所有window对应的布局参数
  • mDyingViews存储正在删除的view对象
  1. 通过ViewRootImpl来更新并完成添加过程

Window删除过程

通过findViewLocked来查找待删除的View索引,查找过程建立数组索引遍历,在调用removeViewLocked进一步删除。

删除提供两个接口,removeView与removeViewImmediate,异步与同步删除,同步删除window会发生错误,而异步删除由viewRootImpl的die方法完成,die发送一个请求删除消息后就返回,将view添加到DyingViews中。

Window更新过程

调用updateViewLayout让新的LayoutParams替换老的LayoutParams,再更新viewRootImpl中的LayoutParams,并在viewRootImpl中的scheduleTraversals对view进行重新布局,还会通过windowSession来更新window视图,这个过程由relayoutWindow具体实现,它是IPC进程。

Android 8.0源码

WMS创建涉及三个线程,分别是system_server、android:display、android:ui之间关系

  1. 首先system_server线程中执行了systemServer的startOtherServices方法,在该方法中调用WMS的main方法,main方法会创建WMS,创建过程在android:display线程中实现,创建WMS优先级更高,因此system_sever线程要等WMS创建完成后,处于等待状态的system_server线程才会被唤醒从而继续执行**
  2. 在WMS构造方法中会调用WMS的initPolicy方法,在该方法中又会调用PowerManagerService的init方法,PWM的init方法会在android:ui线程中运行,它的优先级要高于android:display线程,因此android:display要等待init方法执行完毕后,android:display才会被唤醒继续执行
  3. PWM的init方法执行完毕后,android:display线程就完成了WMS创建,等待的system_server线程被唤醒继续执行WMS的main方法后的逻辑,比如WMS的displayReady方法用来初始化屏幕显示信息。

WMS重要成员

  1. mPolicy:WindowManagerPolicy

WindowManagerPolicy是窗口管理策略接口类,用来定义一个窗口策略遵循的规范,并提供WM所特定的UI行为,具体实现类为PhoneWIndowManager,类在WMS创建时被创建。

  1. mSessions:ArraySet

元素类型为Session,主要用于进程间通信,并且每一个应用程序都会对应一个Session

  1. mWindowMap:WindowHashMap
  • key值为IBindervalue值为WindowsState,用来保存WMS各种窗口集合
  1. mFinishedStarting:arrayList

元素类型为AppWindowToken,是WindowToken子类,作用:

  • 窗口令牌,当应用程序想要向WMS申请创建窗口时,需要出示有效的令牌,应用程序每一个activity都对应一个AppWindowToken
  • 会将同一个组件(比如activity)的窗口(WindowState)集合在一起,方便管理

mFinishedStarting就是用于存储已经完成启动的应用程序窗口的AppWindowToken列表;

Window添加过程

  1. 对所要添加的窗口进行检查,如果窗口不满足一些条件,就不会执行之后的逻辑
  2. WindowToken相关处理,比如有的窗口类型需要提供WindowToken,没有提供就不会执行后面逻辑,有的窗口类型需要由WMS隐式创建WindowToken
  3. WindowState创建相关处理,将WindowTokenWindowState相关联
  4. 创建与配置DisplayContent,完成窗口添加到系统前的准备工作

Window删除过程

  1. 检查删除线程的正确性,不正确抛异常
  2. ViewRootImpl列表,布局参数列表与View列表删除与Window对应的原色
  3. 判断是否可以直接执行删除操作,不能则推迟删除操作
  4. 执行删除操作,清理与释放与Window相关的一切资源

WindowManagerServiceWindowManager的管理者的

WMS职责

  1. 窗口管理

它是窗口管理者,负责窗口启动、添加、删除,另外窗口大小与层级也是由它进行管理。管理的核心成员有:DisplayContent、WindowToken与WindowState

  1. 窗口动画

窗口间进行切换时,使用动画更加炫酷些,是由WMS的动画子系统负责,管理者为WindowAnimator

  1. 输入系统的中转站

通过窗口触摸而产生的触摸事件,InputManagerService会对触摸事件进行管理,寻找最合适的窗口来处理触摸反馈事件

  1. Surface管理

窗口并不具有绘制功能,每个窗口都需要一块Surface来供自己绘制,为每个窗口分配Surface由WMS完成的

Activity的window创建过程

由activity的setContentView看出,它的具体实现交给了PhoneWindow

  1. 如果没有DecorView就创建
  2. 将view添加到DecorViewmContentParent
  3. 回调activityonContentChanged方法通知activity的视图发生改变

Dialog的window创建过程

  1. 创建window
  2. 初始化DecorView并将diaolog视图添加到DecorView
  3. DecorView添加到window

必须采用activity的context,否则采用application的context会报错.是由于没有应用token导致,而token只有activity有,可以指定为系统类型window就可以正常弹出。

  1. dialog.getWindow().setType(LayoutParams.TYPE_SYSTEM_ERROR)
  2. 声明权限 : android.permission.SYSTEM_ALTER_WINDOW

Toast的window创建过程

它有定时功能,采用handler,内部有两类IPC

  1. Toast访问NotificationManagerService
  2. NotificationManagerService回调Toast的TN接口

Toast的延时时长只有2s与3.5s

四大组件工作过程

**Android 7.0与8.0区别是,与AMS进行进程间通信采用的AIDL技术去掉了此前一直沿用的activityManagerProxyapplicationThreadProxy等代理类。

Activity的工作过程

  1. 启动activity真正实现由activityManagerNative,getDefault()startActivity方法完成,AMS是一个Binder,这个binder对象采用单例模式对外提供,Singletn是一个单例封装类,第一次调用它的get通过create来初始化AMS的binder对象。

ActivityManager.getDefault()实际上是ActivityManagerService(AMS)

  1. Activity启动过程在ActivityStackSupervisorActivityStack之间传递顺序 startActivityMayWait -> startActivityLocked -> startActivityUncheckedLocked ->resumeTopActivitiesLocked -> resumeTopActivityInnerLocked  ->startSpecificActivityLocked -> realStartActivityLocked 其中resumeTopActivitiesLockedresumeTopActivityInnerLockedActivityStack中,其余在ActivityStackSupervisor
  2. Activity的启动过程由ActivityThreadhandleLaunchActivity方法实现。

最后performLaunchActivity完成activity对象创建与启动过程。

performLaunchActivity完成事:

  • ActivityClientRecorf中获取待启动activity的组件信息
  • 通过Instrumentationnewactivity方法使用类加载器创建activity对象
  • 通过LoadedApkmakeApplication来创建Application对象,其中Application不会重复创建
  • 创建ContextImpl对象通过activity的attach来完成一些重要数据初始化,ContextImpl是context的具体实现,在attach方法中,activity还会完成window创建并建立关联window
  • 调用activity的onCreate方法

Service

Service的启动过程

  1. Service启动过程从ContextWrapper的startService开始,从ContextWrapper实现看出,大部分操作通过mBase实现,是一个桥接模式。然后调用ContextImplstartService实现,又调用了ActivityManager.getDefault()实际上是ActivityManagerService(AMS)的startServic方法,Services对象类型ActiviteServices是一个辅助AMS进行Service管理,包括启动,绑定,停止。
  2. ActiviteServicesstartServiceLocked尾部的startServiceInnerLocked,ServiceRecord描述一个Service记录,贯穿整个启动过程,又交给了bringUpServiceLocked,然后交给realStartServiceLocked处理,方法里又调用了ApplicationThread的scheduleCreateService通过发消息交给ActivityThread的handleCreateService处理

handleCreateService做的事:

  1. 通过类加载器创建service实例
  2. 创建Application,调用onCreate
  3. 接着创建ContextImplservice的attach建立二者之间关系
  4. 最后调用service的oncreate将servic对象存储在activityThread列表中

Service的绑定过程

ContextWrapper开始,然后调用ContextImpl的bindService,调用bindServiceCommon方法做了两件事:

  1. 将客户端的ServiceConnection对象转化为ServiceDispatcher.InnerConnection对象,因为服务绑定可能跨进程
  2. 接着bindServiceCommon通过AMS完成Service的具体绑定,调用AMS的bindService方法

绑定过程调用ActivityThread的scheduleBindService,实现在ActiviteServicesrequestServiceBindingLocked方法中。然后ApplicationThread的scheduleBindService发消息进行绑定,调用handleBindService,根据servicetoken取出service对象,然后调用onBinder方法,就绑定了,但客户端不知道是否成功连接service还需要调用AMS的publishService方法,多次绑定相同的service,onBinder方法只会执行一次。

BroadcastReceiver的工作过程

广播注册过程

  1. 静态注册有PackManagerService来完成整个注册过程
  2. 动态注册从ContextWrapper的registerReceiver方法开始,然后调用ContextImplregisterReceiverInternal方法, 首先从mPackageInfo中获取IIntentReceiver对象,然后采用跨进程方式向AMS发送广播注册,之所以采用IIntentReceiver,由于BroadcastReceiver作为android不能直接跨进程。getReceiverDispatcher方法重新创建一个ReceiverDispatcher对象并将其板寸的InnerReceiver对象作为返回值返回,其中InnerReceiver和BroadcastReceiver在ReceiverDispatcher的构造方法中保存

广播真正实现在AMS中,最终把远程的InnerReceiver对象以及IntentFilter对象存储起来。

广播发送与接收过程

广播发送仍然是从ContextWrappersendBroadcast开始,交给ContextImplsendBroadcast,然后在AMS的broadcastIntent方法中开始,调用broadcastIntentLocked方法。

intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES)表示在android 5.0中,默认情况下广播不会发送给已经停止的应用,而FLAG_INCLUDE_STOPPED_PACKAGES表示包含已经停止的应用,广播会发送给停止的应用。当这两种状态共存,以FLAG_INCLUDE_STOPPED_PACKAGES为准。

一个应用处于停止有两种情形:

  1. 应用安装后未运行
  2. 应用被手动或其他应用强停了 从android3.1开始,处于停止的应用无法接收到开机广播

broadcastIntentLocked内部,根据intent-filter查找匹配广播接收者并经过一系列过滤,将满足条件的添加到BroadcastQueue中。然后在BroadcastQueue中调用scheduleBroadcastsLocked方法,通过handler发消息让processNextBroadcast处理。

调用LoadedApk类的performReceive方法,创建Args对象,它实现了Runnable接口,run方法中:

final BroadcastReceiver receiver = mReceiver;                                                           receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);

这个时候BroadcastReceiver的onReceive方法执行,应用已经接收广播了。

ContentProvider工作过程

  1. contentProvider所在的进程启动时,contentProvider会同时启动并发布AMS中,**注意:这个时候ContentProvider的onCreate要先于Application的onCreate方法执行。
  2. 当一个应用启动时,入口方法为activityThread的main方法,main方法是一个静态方法,在main方法中创建activityThread实例并创建主线程的消息队列,然后在activityThreadattach方法中会远程调用AMS的attachApplication方法并将ApplicationThread对象提供给AMS。ApplicationThread是一个Binder对象,主要用于ActivityThread与AMS之间通信。

在AMS的attachApplication方法中,会调用applicationThread的bindApplication方法,同样跨进程完成,bindApplication逻辑经过applicationThreadmH handler切换到activitThread中执行,具体方法是handleBinderApplication,方法中activityThread会优先加载contentProvider然后在调用application的oncreate方法。

  1. ContentProvider一般都是单实例,是由android:multiprocess决定,为false是单实例,为true多实例。
  2. ActivityThread的handleBinderApplication,则完成Application创建以及ContentProvider创建:
  • 创建ContentImplInstrumentation
  • 创建Application对象
  • 启动当前进程的ContentProvider并调用onCreate方法
  • 调用ApplicationonCreate方法

Android消息机制

handler运行底层需要MessageQueueLooper支撑

  1. MessageQueue使用来存储消息的,以队列形式插入与删除消息,内部存储结构并不是真正的队列,而是用单链表数据结构来实现的消息存储。
  2. Looper用来处理消息,以无限循坏的方法是查看是否有新的消息,有的话就进行处理,否则一直处于等待。还有一个特殊的概念ThreadLocal,作用可以在每个线程中存储数据。在handler内部可以通过ThreadLocal来获取每个线程的Looper,它可以在不同线程互不干扰存储并提供数据。 如果线程没有默认的Looper,那么使用handler就必须创建Looper。ActivityThread被创建时会初始化Looper,这就是默认可以在主线程中使用handler。
  3. 通过handler的post方法将一个runnable投递到handler内部的Looper中去处理,也可以通过send发消息。当handler的send方法被调用,它会调用MessageQueue的equeueMessage方法将消息存储到队列中,然后Looper就会处理这个消息,然后handlerMessage方法就会调用。Looper运行在创建handelr所在的线程中,这样handler的处理逻辑就会在创建handler线程中执行。

ThreadLocal使用场景

  1. 当某些数据以线程为作用域并且不同线程具有不同数据副本使用ThreadLocal,比如要获取当前线程的Looper,但不同线程有不同Looper。
  2. 复杂逻辑下的对象传递,比如监听传递

采用ThreadLocal可以让监听器作为线程内的全局对象存在,线程内部只需通过get方法获取监听器。

MessageQueue工作原理

主要包含插入与读取,分别对应equeueMessagenext方法

从源码中看出equeueMessage的实现主要是单链表的插入操作。

next方法是一个无限循坏方法,如果这个消息队列没有消息,next就会一直阻塞在这里,当有消息,就会返回这条消息,并将从单链表移除。

Looper工作原理

查看是否有消息,有就处理,没有就一直阻塞。

通过Looper.prepare()创建Looper,Looper.loop()开启消息循坏

可以在主线程中创建Looper调用prepareMainLooper,调用getMainLooper在主线程获取Looper。

调用quitquitSafely来退出Looper。区别:

  1. quit直接退出Looper
  2. quitSafely设定退出标记,只有消息处理完毕才会安全退出。手动创建Looper,那么在所有事情处理完毕后调用quit来退出Looper来终止消息循坏,否则一直处于等待状态。

loop方法是一个死循环,只有MessageQueue的next方法返回为空时,才会跳出循坏,所以不使用时必须通过quit或者quitSafely退出循环,否则会造成内存泄漏等其他问题

Handler工作原理

消息发送与接收,可以通过post与send来实现,post方法最终通过send方法实现。handler发送消息仅仅向消息队列中插入一条消息。

handler处理消息过程

  1. 检查messagecallback是否为空,不为空,就通过handleCallback处理
  2. 为空检查mCallback是否为空,不为空就调用mCallback.handleMessage处理
  3. 最后调用handler的handlerMessage处理

handler还有一个特殊的构造方法,通过特定的Looper构造Handler,如果当前线程没有Looper,就会抛异常。

主线程消息循坏

  1. android主线程是ActivityThread,入口为main方法,prepareMainLooper创建主线程Looper与messageQueue.
  2. ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信方式完成ActivityThread请求回调ApplicationThread的binder方法,然后ApplicationThreadH发送消息,H收到消息将ApplicationThread逻辑切换到主线程执行。**

Android线程与线程池

Volatile

volatile :保证可见性,有序性,但不能保证原子性

使用volatile必须具备以下两个条件:

  1. 对变量写操作不会依赖于当前值
  2. 该变量没有包含在其它变量的不变式

第一个条件就是:自增,自减

第二个条件就是:包含一个不变式:下界总是小于或等于上界

voliate可以用于很多场景:

  • 状态标志
  • 双重检查模式

Java 阻塞队列

  1. ArrayBlockingQueue:由数组结构组成的有界阻塞队列; 按照先进先出原则对元素排列,默认情况下不保证公平访问队列,公平访问队列指的是:阻塞所有的生产者与消费者,当队列可用时,按照阻塞的先后顺序进行访问,先阻塞生产者,往里面插入元素;阻塞消费者,从队列里面获取元素;
  2. LinkedBlockingQueue:由链表构成的有界阻塞队列,先进先出原则对元素进行排序;只有当队列的缓存区达到缓存容量最大值,才会阻塞队列,直至消费者从队列消费一份数据,生产者线程才会被唤起;
  3. PriorityBlockingQueue:支持优先级排序的无界阻塞队列;默认元素升序排序;可以自定义compareTo()方法进行排序;
  4. DelayQueue:延时获取元素的无界阻塞队列;创建元素可以指定元素的时间,只有到元素到期时,才会取走元素;
  5. SynchronousQueue:不储存元素的阻塞队列;每插入操作必须等待另一个线程的移除操作;因此队列中没有任何元素;
  6. LinkedTransferQueue:由链表构成的无界阻塞队列;
  7. LinkedBlockingDeque:由链表组成的双向阻塞队列;可从队列的两端插入和移除元素;

AsyncTask

AsyncTask是一个轻量级异步任务类,在线程池中执行后台任务,将最终结果传递给主线程中,并在主线程中更新UI,它封装了Thread与handler

AsyncTask线程池配置的参数:

  1. 核心线程数等于CPU核心数+1
  2. 最大线程数为CPU核心数2倍+1
  3. 核心线程无超时时长,非核心线程超时时长为1秒

4.任务队列容量128

内部方法:

  1. onPreExecute在主线程中执行,异步任务开始前调用,用于做准备工作
  2. doInBackground在线程池中执行,调用publishProgress更新任务,publishProgress调用onProgressUpdate方法,返回计算结果给onPostExecute
  3. onProgressUpdate在主线程中执行,当后台任务发生改变时,此方法调用
  4. onPostExecute在主线程执行,result参数是后台任务返回值

AsyncTask一些条件限制:

  1. AsyncTask必须在主线程中加载,第一次访问AsyncTask必须发生在主线程,在android4.1及以上已经被系统自动完成,在android5.0源码中,查看activityThread的main方法,它会调用asyncTask的init方法,这就是它必须在主线程中加载的条件。 从源码可知sHandler是一个静态Handler对象,能够将执行环境切换到主线程中,这就要求sHandler在主线程中创建,静态成员会在加载类时进行初始化,变相要求AsyncTask必须在主线程中加载。
  2. AsyncTask对象必须在主线程中创建
  3. execute必须在UI线程调用
  4. 不要再程序中直接调用onPreExecute,doInBackground,onProgressUpdate方法
  5. 一个AsyncTask对象只能执行一次,只能调用一次execute方法,否则会报异常
  6. 在android1.6以前,它是串行执行,android1.6开始采用线程池处理并行任务,从android3.0开始,采用串行执行任务,仍然可以调用executeOnExecute方法并行执行任务

AsyncTask有两个线程池(SerialExecutor与THREAD_POOL_EXECUTOR)和一个IntentHandler,SerialExecutor用于任务排队,THREAD_POOL_EXECUTOR用于真正执行任务,IntentHandler用于将执行环境从线程池中切换到主线程。

HandlerThread

它继承自Thread,在run方法中通过Looper.prepare创建消息队列,通过Looper.loop开启消息循坏。普通Thread的run方法执行一个耗时任务,而它内部创建消息队列,外界需要通过handler的消息方式来通知它执行一个具体的任务。**

使用场景Intentservice,不需要使用handlerThread时,使用quit,quitsafely终止执行。

IntentService执行后台耗时任务,当任务执行后它会停止。适合高优先级的后台任务。

onHandlerThread方法执行结束后,IntentService采用stopSelf(int startId)来尝试停止服务,而使用stopSelf()方法则会立刻停止所有服务,而stopSelf(int startId)等待所有消息都处理完毕后才会终止,尝试停止服务之前判断最近启动的服务次数是否与startId相等,相等则停止。

线程池

线程池优点

  1. 重用线程池,避免因线程创建与销毁而带来的性能开销
  2. 有效控制线程的并发数,避免因线程互相抢占资源而导致阻塞现象
  3. 能够对线程进行简单管理,提供定时执行与间隔循坏功能

Executor

Executor为一个接口,真正实现是ThreadPoolExecutor ThreadPoolExecutor内部参数:

  1. coorPoolSize 核心线程数
  2. maximumPoolSize 最大线程数,达到这个数值,后台任务会阻塞
  3. keepAliveTime 非核心线程闲置时的超时时长
  4. unit 时间单位
  5. workQueue 任务队列,线程池的execute提交的Runnable对象会存储在这个参数中
  6. threadFactory 线程工厂
  7. 饱和策略 RejectedExecutionHandler:饱和策略,这是当任务队列与线程池都满的情况下,所采取的应对策略,默认是AbordPolicy,表示无法处理新任务,抛出RejectedExecutionExecption异常,还有三种策略:
    • CallerRunsPolicy:用调用者所在的线程来处理任务,提供简单的反馈机制,能够减缓新任务的提交速度
    • DiscardPolicy:不能执行的任务,并将任务删除
    • DiscardOldestPolicy:丢弃列表最近的任务,并执行该任务

线程池分类

  1. FIxedThreadPool

线程数量固定的线程池,当线程处于空闲状态,它们并不会回收,只有核心线程,能够快速响应外界请求,任务队列没有大小限制

  1. CacheThreadPool

只有非核心线程,线程数无限大,空闲线程超时时长为60s,任务队列为SynchronousQueue,是一个无法存储元素的队列。适合执行大量耗时较少的任务。

  1. ScheduleThreadPool

核心线程数量固定,非核心线程数没有限制,用于执行定时任务与具有固定周期的重复任务。采用DelayWorkQueue是无界的。

  1. SingleThreadExecutor

只有一个核心线程,没有非核心线程,任务都在同一个线程中顺序执行,无需考虑同步问题。

  1. Excecutors.newWorkStealingPool:JDK8引入,创建持有足够线程的线程池支持给定的并行度,并通过使用多个队列减少竞争,把CPU数量设置为默认的并行度。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 线程优化

    Process中定义,值越小,优先级越高,默认是THREAD_PRIORITY_DEFAULT 0

    Yif
  • Java 锁分类

    乐观锁是一种乐观思想,认为读多写少,遇到并发的可能性低,每次拿数据时候并不会上锁,因为认为不会被别人修改。但是更新的时候会判断有没有人会更新这条数据,采取写的时...

    Yif
  • Activity 基础知识

    类加载方案需要重启App后让ClassLoader重新加载新的类,为什么需要重启,因为类是无法卸载的,要想重新加载类就需要重启App,因此采用类加载方案的热修复...

    Yif
  • SpringBoot线程池的创建、@Async配置步骤及注意事项

    最近在做订单模块,用户购买服务类产品之后,需要进行预约,预约成功之后分别给商家和用户发送提醒短信。考虑发短信耗时的情况所以我想用异步的方法去执行,于是就在网上看...

    IT大咖说
  • 干货 | Tomcat 连接数与线程池详解

    在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。

    Java技术栈
  • Java多线程编程-(6)-你还在使用wait/notify实现进程间的通信吗?

    在《Java多线程编程-(5)-线程间通信机制的介绍与使用》已经学习了,可以使用方法wait/notify 结合同步关键字synchronized实现同步和线程...

    Java后端技术
  • 优化指南,详解 Tomcat 的连接数与线程池

    在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。

    java思维导图
  • 干货|Tomcat 连接数与线程池详解前言

    在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。

    美的让人心动
  • 详解tomcat的连接数与线程池

    在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。

    小柒2012
  • 详解 Tomcat 的连接数与线程池

    前言 在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。 在前面的文章 详...

    Java高级架构

扫码关注云+社区

领取腾讯云代金券