Gallery2分析

news2024/11/15 5:05:46

本文分析的代码基于 Android 4.x

文章目录

    • Gallery2介绍
      • 基本数据
      • 数据源
      • DataManager数据管理
      • LocalSource本地数据源
      • Media数据的加载过程
      • Gallery2页面结构
      • ActivityState
      • StateManager
      • GalleryAppImpl
      • AbstractGalleryActivity
      • GalleryActivity
      • AlbumSetPage数据加载和渲染过程分析
      • 第四步 获得 AlbumSet 数据后,渲染器开始渲染 SlotView
    • ThreadPool 线程池

Gallery2介绍

Gallery2主要功能是实现Android系统本地存储以及网络存储中的媒体(图片&视频)资源的浏览,媒体信息,显示和更多操作(删除、分享、选择、缩放、编辑等)。

Gallery2界面的生成和普通的应用程序不同,普通应用程序一般就一个界面对应一个activity,搭配布局xml或代码来实现界面的显示,而Gallery2没有用到android的UI系统,而是通过openGL画出来,只用到了一个acitivity.、

Android4.x后的Gallery2的主要组成有:

①AlbumSetPage 所有专辑界面,点击Gallery图标后进入图库所显示的第一个页面;
②AlbumPage 单个专辑界面,AlbumSetPage的一个子集,显示该专辑内的所有图片,专辑通过文件夹来区分;
③PhotoPage 单张图片界面,可用通过AlbumPage进入,也可以通过外部调用浏览单张图片。

基本数据

Gallery2中描述单个媒体对象的数据结构图如下:

Gallery2中描述一组媒体对象的数据结构图如下:

MediaObject 数据渲染的最小单位,它包含丰富的衍生类。MediaObject定义了媒体数据最基本的信息,如SupportedOperations,SupportedOperations定义这个媒体文件支持的操作,如是否可以delete/share/rotate等。定义了最基本的Path路径,用于表示媒体对象的存储地址;

MediaItem MediaObject的衍生类,MediaObject的封装,是单个媒体的抽象,代表一张图片或者一个视频。在此抽象类中,定义getMimeType()/getWitdh()/getHeight()等抽象方法。;

LocalMediaItem MediaItem的衍生类,对本地MediaItem的抽象,代表一张本地图片或者一个本地视频。在此抽象类中,添加定义了bucketId/dataDirty; bucketId由文件夹的绝对路径的hashCode来表示,代表一个专辑,是专辑的索引。通过GalleryUtils的getBucketId可以获得传入路径的bucketId。

LocalImage LocalMediaItem的子类,表示一个本地存储的图片。内部定义了一个ITEM_PATH=“/local/image/item”。首先LocalImage的初始化有两种,一种是通过直接传入cursor对象来初始化这个Image对象,另外一种是通过传入id的形式来查询外部存储的数据库,得到cursor,进而初始化这个Image对象。

MediaSet 和MediaItem一样是MediaObject的衍生类,是一个类目录的数据结构,是一组媒体文件的抽象。它提供的主要基础接口有getMediaItemCount, getMediaItem, getSubMediaSetCount, getSubMediaSet, getTotalMediaItemCount. 还定义了getCoverMediaItem来获得一组图片或视频的封面。

LocalAlbumSet 继承于MediaSet,是所有图片和视频专辑的集合。其内部定义了三个path,分别是PATH_ALL,PATH_IMAGE,PATH_VIDEO.其内部定义了mAlbums用来保存专辑列表。LocalAlbumSet是AlbumSetPage的单位。

LocalAlbum 继承于MediaSet,代表一个bucket(目录)下的所有的media items。提供MediaItem的查询,删除等操作。LocalAlbum是AlbumPage的单位。

数据源

Gallery2中引入数据源的概念,由DataManager负责管理,目的是在不同的显示界面,能通过DataManager获得一个合适的数据源来初始化自己的数据。Gallery2中主要定义的数据源有ComboSource(组合源), PicasaSource(Picasa源),LocalSource(本地源), ClusterSource(簇源), UriSource(URL源),FilterSource(过滤源)。这些数据源有一个共同的基类MediaSource, MediaSource是对数据源的抽象,它里面主要定义了数据源的基本组成,如定义了数据源的唯一标识prefix, prefix后面会讲到。

DataManager数据管理

在详细分析数据源的组成结构之前,首先来分析下DataManager。DataManager是用来管理整个系统中的所有media sets(集合)和media item。DataManager在Gallery Application启动时就创建并且初始化,可以通过GalleryAppImpl的getDataManager方法来获得DataManager实例,DataManager的初始化做了以下事情:

addSource(new LocalSource(mApplication));

addSource(new PicasaSource(mApplication));

addSource(new ComboSource(mApplication));

addSource(new ClusterSource(mApplication));

addSource(new FilterSource(mApplication));    

addSource(new UriSource(mApplication));

可以看出,DataManager实例化的同时也创建了所有数据源实例,并把它们加入自身维护的一个SourceMap中,提供存取操作。SourceMap中保存的索引是上面讲到的prefix。Prefix是数据源的唯一标识,在数据源的构造方法中赋值。如LocalSource的prefix为”local”, ComboSource的prefix为”combo”。

    void addSource(MediaSource source) {

        if (source == null) return;

        mSourceMap.put(source.getPrefix(), source);

}

DataManager不仅提供了丰富的数据操作接口,同时定义了一组代表数据集合的PATH:

TOP_SET_PATH = "/combo/{/local/all,/picasa/all}";  //表示用户能看到的最顶端的数据集合

TOP_IMAGE_SET_PATH = "/combo/{/local/image,/picasa/image}";  //表示用户能看到的最顶端的图片数据集合

TOP_VIDEO_SET_PATH = "/combo/{/local/video,/picasa/video}";  //表示用户能看到的最顶端的视频数据集合

TOP_LOCAL_SET_PATH = "/local/all";  //表示用户能看到的最顶端的本地数据集合

TOP_LOCAL_IMAGE_SET_PATH = "/local/image";  //表示用户能看到的最顶端的本地图片集合

TOP_LOCAL_VIDEO_SET_PATH = "/local/video";  //表示用户能看到的最顶端的本地视频集合

数据范围的比较如下:
TOP_SET_PATH > String TOP_IMAGE_SET_PATH = TOP_VIDEO_SET_PATH > TOP_LOCAL_SET_PATH > TOP_LOCAL_IMAGE_SET_PATH = TOP_LOCAL_VIDEO_SET_PATH
//插入一张DataManager的一张类结构图

LocalSource本地数据源

LocalSource表示本地存储器中的所有Media数据源,负责管理Local Media数据集。从它的createMediaObject方法(继承于MediaSource)可以看出,它可以根据传入的path路径,创建出LocalAlbumSet,LocalAlbum,LocalMergeAlbum,LocalImage,LocalVideo所有本地媒体数据相关的数据集合以及单个媒体文件。


    public MediaObject createMediaObject(Path path) {

        GalleryApp app = mApplication;

        switch (mMatcher.match(path)) {

            case LOCAL_ALL_ALBUMSET:

            case LOCAL_IMAGE_ALBUMSET:

            case LOCAL_VIDEO_ALBUMSET:

                return new LocalAlbumSet(path, mApplication);

            case LOCAL_IMAGE_ALBUM:

                return new LocalAlbum(path, app, mMatcher.getIntVar(0), true);

            case LOCAL_VIDEO_ALBUM:

                return new LocalAlbum(path, app, mMatcher.getIntVar(0), false);

            case LOCAL_IMAGE_ITEM:

                return new LocalImage(path, mApplication, mMatcher.getIntVar(0));

            case LOCAL_VIDEO_ITEM:

                return new LocalVideo(path, mApplication, mMatcher.getIntVar(0));

            default:

                throw new RuntimeException("bad path: " + path);

        }

}

那么LocalSource是怎样根据传入的path来生成AlbumSet,还是Album呢?首先我们先来看看LocalSource的构造方法:

   public LocalSource(GalleryApp context) {

        super("local");

        mApplication = context;

        mMatcher = new PathMatcher();

        mMatcher.add("/local/image", LOCAL_IMAGE_ALBUMSET);

        mMatcher.add("/local/video", LOCAL_VIDEO_ALBUMSET);

        mMatcher.add("/local/all", LOCAL_ALL_ALBUMSET);

        mMatcher.add("/local/image/*", LOCAL_IMAGE_ALBUM);

        mMatcher.add("/local/video/*", LOCAL_VIDEO_ALBUM);

        mMatcher.add("/local/all/*", LOCAL_ALL_ALBUM);

        mMatcher.add("/local/image/item/*", LOCAL_IMAGE_ITEM);

        mMatcher.add("/local/video/item/*", LOCAL_VIDEO_ITEM);

}

LocalSource的构造方法中实例化了PathMatcher,并将所有代表local资源相关的path及其类型添加到PathMatcher实例中。这里PathMatcher的作用是维护一个树结构,用于保存path以及匹配path类型。PathMatcher类内部定义一个Node(节点),代表树的一个节点。Node由HashMap以及一个整型kind组成,其中HashMap用来保存路径子段和Node的映射,而整型kind用来保存该节点的类型,如(LOCAL_IMAGE_ALBUMSET/LOCAL_VIDEO_ALBUMSET)等。先来说一下PathMatcher的实现过程,在PathMatcher的构造方法中,首先创建了一个名为Root的树的根节点,这个Root的根节点作为match操作的入口。另外,PatchMatcher通过add方法,先将传入的path路径以”/”为分割符创建segments数组,然后通过segments数组的元素构造树结构,并给最后一个节点的kind类型赋值,表示从根节点到该节点生成的path代表哪个类型的媒体结构。

匹配的过程如下:

path = “/local/image/item/10001”

序列为:

[local][image][item][10001]

二叉树查询:

Kind=LOCAL_IMAGE_ITEM, 生成LocalImage, id=10001

Media数据的加载过程

我们从点击Gallery2图标进入图片专辑页面这个过程为例,描述一下Local数据的加载过程。

首先点击图库图标进入GalleryActivity(旧版本或者命名为Gallery),这个Activity是整个图库程序的入口,非外部ACTION_VIEW调用下,调用startDefaultPage启动AlbumSetPage(就是我们打开Gallery2后见到的第一个专辑页面),这时传入给AlbumSetPage一个名为media-path的参数,media-path值为"/combo/{/local/all,/picasa/all}",这个是一个combo类型的path,表示需要显示local以及picasa两个组合的所有的媒体文件,具体的解析步骤如下:

(1)在AlbumSetPage的initializeData方法取出media-path,mediaPath = /combo/{/local/all,/picasa/all}

(2)AlbumSetPage通过DataManager实例解析出由两个segments组成的url

Segments[0]:combo

Segments[1]:{/local/all,/picasa/all}

(3)第二部解析出来path的prefix(前缀)是bombo,DataManager通过这个prefix取得对应的数据源,这里获得的数据源是ComboSource

(4)DataManager调用ComboSource的createMediaObject方法来初始化ComboAlbumSet实例返回到AlbumSetPage,与此同时,在构造ComboAlbumSet时,继续分拆大括号里的/local/all和/picasa/all,生成LocalSource实例和EmptySource实例。

(4.1)分拆/local/all,创建LocalSource数据源,生成LocalAlbumSet实例。

Segments[0]:local

Segments[1]:all

current path=/local/all Prefix=local

(4.2)分拆/picasa/all,创建PicasaSource数据源,生成EmptyAlbumSet实例

Segments[0]:picasa

Segments[1]:all

current path=/picasa/all Prefix=picasa

(1)~(4)是AlbumSetPage中initializeData调用获取mMediaSet所做的事情,这里返回的mMediaSet实例就是指向一个由ComboSource数据源创建的ComboAlbumSet实例,后面这个mMediaSet的所有实现都可以在ComboAlbumSet中找到。获得mMediaSet后,initializeData里继续做的事情有,用mMediaSet初始化mSelectionManager来管理这个界面的所有选择操作;用mMediaSet初始化AlbumSetDataLoader实例,作为数据适配器,数据取自mMediaSet,这里生成的实例名叫mAlbumSetDataAdapter,也很形象,数据源和适配器都准备完毕后,mAlbumSetView通过setMode方法将适配器传入渲染器中。

Gallery2页面结构

Android 4.x的Gallery2主要由AlbumSetPage, AlbumPage, PhotoPage三个页面组成,它们都继承自ActivityState。ActivityState会在下面描述,实际上可以把ActivityState看作是Gallery2三个页面中的其中一个Activity状态,也可以理解成当前Activity以哪种界面显示。 一般我们见到的程序窗口,大多是用Actiivty来实现的,而Gallery2中,实际上只有一个Activity,叫AbstractGalleryActivity,Gallery2通过ActivityState和StateManager来实现三个页面在同一个Activity中显示和切换。

在讲三个主要界面之前,先来了解一下Gallery2这个特殊的显示结构。

ActivityState

Gallery2中定义了ActivityState这样一个抽象类,表示一个页面的状态,或者可以理解成在Galelry2中,这就是一个界面。每一个子页面(如AlbumSetPage)都是ActivityState的衍生类。

ActivityState有一整套类似Activity一样的生命周期,整个生命周期全部由StateManager来管理。

ActivityState的结构和Activity十分的类似,可以说是一个精简的Activity。和StartActivity来启动一个Activity不同,ActivityState通过StateManager的startState方法来开启,整个生命周期的管理由StateManager来管理。

① ActivityState和Activity的类结构对比

StateManager

StateManager的作用类似于ActivityManager,是每个子页面(ActivityState)的管理类,负责子页面的切换,命令的执行,页面的刷新显示等职能,是ActivtyState的调度器。Gallery将所有页面操作命令交给StateManager来执行,StateManager再将命令分发给当前的子页面执行。

StateManger可以通过AbstractGalleryActivity的getStateManager方法获得。StateManager中包含一个堆栈mStack,用于保存ActivtyState,和Activity一样的实现方式,子页面先进后出,当切换到新的子页面时,当前页面压入堆栈中。当用户按back返回时,当前页面出栈并销毁,前一个子页面做为当前显示界面。

StateManager是一个桥梁,将真正的Actiivty(GalleryActivity)和ActivityState状态关联起来。当启动Gallery2时,首先执行GalleryActivity的onCreate方法,然后调用StateManager的startState方法加载AlbumSetPage,StateManager在startState方法中,首先

会判断当前堆栈栈顶是否为空,由于AlbumSetPage是第一页面,此时堆栈为空,因此AlbumSetPage会被压入堆栈中,并在栈顶显示。在将AlbumSetPage压入栈后,会依次执行AlbumSetPage的onCreate和onResume方法。这样一来给人的感觉就是,我启动了AlbumSetPage,同样会执行onCreate和onResume方法,从Actiivty到ActivityState状态的同步是通过StateManager来传递的(图9)。

同时SateManager也是ActivityState之间切换的桥梁,举个例子:从AlbumSetPage进入AlbumPage的过程中,在AlbumSetPage中通过调用SateManager的startStateForResult(类似于StartActivityForResult,含返回值)方法切换到AlbumPage,此时AlbumSetPage会被压入堆栈,执行onPause方法。并建立AlbumPage并将其进栈,执行onCreate, onResume方法,最终AlbumPage在栈顶显示。当用户执行back事件后跳回AlbumSetPage页面,AlbumPage出栈并执行onStop和onDestory方法。

Activity到ActivityState状态的传递过程

GalleryAppImpl

GalleryAppImpl继承于Application并实现了GalleryApp接口,Gallery2应用程序启动后首先执行GalleryAppImpl,GalleryApp接口抽象了Gallery2作为图库所要实现的最基本操作,例如getDataManager获取数据管理器加载图片视频数据,getImageCacheService获取图片缓存服务,getDownloadCache获取下载缓存,getThreadPool获取线程池等。

GalleryAppImpl的onCreate方法主要执行GalleryUtils工具类的initialize初始化方法来获得手机屏幕的分辨率大小等信息。

AbstractGalleryActivity

AbstractGalleryAcitivty是Gallery2最重要的一个Activity,Gallery2只有一个Actiivty用于显示,它就是AbstractGalleryActivity,它的派生类GalleryActivity是程序的主入口。AbstractGalleryActivity实现了GalleryContext接口,包括StateManager的初始化,在StateManager的初始化中传入AbstractGalleryActiivty的对象引用,这样就StateManager以及它的所有子类都可以通过该引用调用AbstractGalleryActivity提供的方法。

同时AbstractGalleryActivity提供很多实用方法,如调用getDataManager得到DataManager实例,通过getGLRoot方法获得GLRootView,通过getGalleryActionBar方法获得GalleryActionBar等。

GalleryActivity

GalleryActivity是AbstractGalleryActivity的衍生类,是Gallery2程序的主入口,在onCreate方法中initializeByIntent来判断调用的方式,调用方式包括ACTION_GET_CONTENT,ACTION_PICK,ACTION_VIEW和default,根据不同的ACTION执行正确的Gallery调用。如在MMS中调用Gallery2添加图片资源,MMS会发出ACTION_PICK广播进入Gallery2选择图片。

如果ACTION不属于以上(直接点击程序图标进入),GalleryActivity执行startDefaultPage方法,该方法调用StateManager的startState方法进入AlbumSetPage,并传入的KEY_MEDIA_PATH值为INCLUDE_ALL,表示在AlbumSetPage中查看所有media。

AlbumSetPage数据加载和渲染过程分析

AlbumSetPage是我们进入Gallery2后的第一个页面,作用是显示所有相册专辑。其结构主要由一个SlotView和Slot组成(如下图)。一个Slot代表一个相册专辑。Slot中包含的内容有:一张封面图片(一般是相册中首张图片资源),相册标题,相册大小等,如果这是一个特殊的相册,例如是一个保存Camera拍照后的相册,在相册封面上还会有Camera图标表示。

AlbumSetPage

**第一步 initializeViews,目的是初始化 SlotView 以及其渲染器 mAlbumSetView。 **首先初始化 mSelectionManager,用于管理选择事件;

初始化 mConfig 获得有关 AlbumSetPage 的运行参数,包括 Slot 的组成元素的颜色,初始化每个 Slot 在 SlotView 中的大小,间隔等 参数;

初始化 AlbumSetSlotRenderer,SlotView 的渲染器,实例名为 mAlbumSetView;

根据 mConfig 和 AlbumSetSlotRenderer 初始化 mSlotView,并给 mSlotView 注册事件监听; 初始化 mActionModeHandler 处理 ActionBar 事件;

最终将 SlotView 添加到 RootPane 中;

第二步 initializeData,目的是实例化数据适配器,并为渲染器提供数据接口。

获得 GalleryActivity 传过来的 mediaPath,并通过 DataManager 的 getMediaSet 方法获得对应的数据源,由于传过来的 mediaPath 为

/combo/{/local/all,/picasa/all},因此得到的是一个 ComboSource 数据源,此数据源另外包含一个 LocalSource 和 EmptySource,后面这 个 mMediaSet 所有的实现都可以在 ComboAlbumSet 中找到;

根据获得的数据源(mediaSet)来实例化名为 mAlbumSetDataAdapter 的 AlbumSetDataLoader 对象,作为数据适配器。数据源和适配 器都准备完毕后,调用 setMode 方法将数据适配器添加到 SlotView 的渲染器 mAlbumSetView 中;

第三步 调用数据适配器加载媒体数据,为后面渲染提供数据支持。
AlbumSetPage 的 onCreate 方法执行完后,在随后执行 onResume 方法中,首先调用数据适配器(AlbumSetDataLoader)的 resume 方 法开始执行数据加载操作。AlbumSetDataLoader 开启一个 ReloadTask 执行数据源 reload 工作。通过层层的 reload,并调用 BucketHelper 查询 MediaProvider 数据库,获得专辑数据并返回。

ReloadTask 的主要功能是循环调用数据源的 reload 方法,进而更新获得专辑数据,一旦获得专辑数量变化后,通过监听器回调的 方式通知 SlotView,告知专辑数,创建相应数量的 slot,这也是我们平时看到的专辑。

数据加载流程图

第四步 获得 AlbumSet 数据后,渲染器开始渲染 SlotView

数据适配器 AlbumSetDataLoader 中的 ReloadTask 执行完后,发送 MSG_LOAD_FINISH 广播通知到 AlbumSetPage 更新界面,并完成

SlotView 渲染前的准备工作,流程如下:

ThreadPool 线程池

线程池的基本思想是一种对象池的思想,系统开辟一块内存空间,里面存放着众多(未死亡)的线程,池中线程的执行调度由池 管理器来处理。当有线程任务时,池管理器会从池中取一个,执行完成后线程对象归池,这样就可以避免反复创建线程对象所带来的 性能开销了,从而节省了系统资源。

用线程池来管理的好处是,可以保证系统稳定性,适用于有大量线程,高工作量的情景下使用,假如现在我们有 2000 张图片资源

加载并显示,如果创建 2000 个线程区加载执行,系统肯定会死掉,线程池就可以避免这个问题,方案是可以用 5 个线程轮流执行,5 个为一组,执行完成后不直接回收,而是等待下次执行,这样对系统的开销就可以大大减少。

Executor 是 Java 工具类,执行提交给它的 Runnable 任务。该接口提供了一种基于任务运行机制的任务提交方法,包括线程使用详 细信息,时序等等 。 Executor 通常用于替代创建多线程。例 如 :你可能会使用以 下 方式来代替创建线 程 集合中的线程

new Thread(new(RunnableTask())).start()。
Executor executor = anExecutor; executor.execute(new RunnableTask1()); executor.execute(new RunnableTask2());
...

不过,Executor 接口并没有严格地要求执行是异步的。在最简单的情况下,执行程序可以在调用者的线程中立即运行已提交的任务:

class DirectExecutor implements Executor {
 
 
public void execute(Runnable r) { r.run();
}
}

更常见的是,任务是在某个不是调用者线程的线程中执行的。以下执行程序将为每个任务生成一个新线程。

class ThreadPerTaskExecutor implements Executor { public void execute(Runnable r) {
new Thread(r).start();
 
}
}

此包中提供的 Executor 实现实现了 ExecutorService,这是一个使用更广泛的接口。ThreadPoolExecutor类提供一个可扩展的 线程池实现。Executors 类为这些 Executor 提供了便捷的工厂方法。

Java 里面线程池的顶级接口是 Executor,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线

程池接口是 ExecutorService。

根据线程池的执行策略,Executor 的 execute()可能在新线程中执行,或者在线程池中的某个线程中执行,也可能是在调用者线程 中执行。ExecutorService 在 Executor 的基础上增加了两个核心方法:

1、Future<?> submit(Runnable task)

2、 Future submit(Callable task)

差异点:这两个方法都可以向线程池提交任务,区别在于 Runnable 执行完 run()有返回值,而 Callable 执行完 call()后有返回值。 共同点:submit 都返回 Future 对象,Future 对象可以阻塞线程直到运行完毕,也可以取消任务执行和检测任务是否执行完毕。 在 executors 类里面提供了一些静态工厂,生成一些常用的线程池: 1、newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任

务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执 行。

2、newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池 的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3、newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲

(60 秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制, 线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。

4、newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

5、newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

下面再介绍下 ThreadPoolExecutor 函数,以便对线程池有进一步认识:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue,

RejectedExecutionHandler handler);

corePoolSize: 线程池维护线程的最少数量

maximumPoolSize:线程池维护线程的最大数量

keepAliveTime: 线程池维护线程所允许的空闲时间

unit: 线程池维护线程所允许的空闲时间的单位

workQueue: 线程池所使用的缓冲队列

handler: 线程池对拒绝任务的处理策略

当一个任务通过 execute(Runnable)方法欲添加到线程池时:

1、如果此时线程池中的数量小于 corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

2、如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue 未满,那么任务被放入缓冲队列。

3、如果此时线程池中的数量大于 corePoolSize,缓冲队列 workQueue 满,并且线程池中的数量小于 maximumPoolSize,建新的线

程来处理被添加的任务。

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

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

相关文章

Python操作MongoDb创建文档及CRUD基本操作

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 Python3数据科学包系列(三):数据分析实战 MongoDB 操作手册----文档…

Access注入---偏移注入 | Mysql注入---DNS注入 | MSSQL---反弹注入

伪静态---假的静态页面判断页面是否为静态&#xff1a;document.lastModified偏移注入使用场景&#xff1a;遇到知道表明&#xff0c;但不知道字段名的情况下使用表.* >&#xff08;核心&#xff09;information_schema.tables&#xff08;去information_schema库里面选中ta…

头戴式耳机怎么戴好看?头戴式耳机正确代法

走在大街上总能看到那么一些人&#xff0c;他们眼神时而朦胧涣散&#xff0c;时而精神奕奕&#xff0c;全身上下始终散发着#请勿打扰#的气息&#xff0c;因为他们都戴着头&#xff01;戴&#xff01;式&#xff01;耳&#xff01;机&#xff01;但是头戴式耳机把头压得扁扁的&a…

UG\NX CAM二次开发 创建加工环境 UF_SETUP_create

文章作者:代工 来源网站:NX CAM二次开发专栏 简介: UG\NX CAM二次开发 创建加工环境 UF_SETUP_create,我们ug编程也是要进去加工模块加载环境,选择模板的这是我们加工编程的第一步 代码实现进入加工环境: void MyClass::do_it() { // 创建加工设置 UF_SETUP…

1300*D. Alice, Bob and Candies(模拟)

Problem - 1352D - Codeforces 解析&#xff1a; 模拟即可。 #include<bits/stdc.h> using namespace std; #define int long long const int N2e55; int t,n,a[N]; signed main(){scanf("%lld",&t);while(t--){scanf("%lld",&n);for(int i…

XPD977协议系列-支持 XPD-LINK™互联 USB 三端口控制器

XPD977 是一款集成 USB Type-C、USB Power Delivery&#xff08;PD&#xff09; 3.0/2.0 以及 PPS、QC3.0/2.0CLASS B 快充协议、华为 FCP/SCP 快充协议、三星 AFC 快充协议、VOOC 2.0 快 充协议、BC1.2 DCP 以及苹果设备 2.4A 充电规范的多功能 USB 三端口控制器&#xff0c;为…

【LeetCode刷题笔记】二维数组

498.对角线遍历 解题思路&#xff1a; 简化问题&#xff0c;首先考虑按照逐条对角线打印元素&#xff0c;而不考虑翻转的情况。 M 行 N 列的二维矩阵总共有 M N - 1 条对角线&#xff08; 右上 -> 左下 &#xff09; 1&#xff09;如何遍历&#xff1a; 从左往右遍历 对角…

【小沐学C++】git和github常见问题汇总

文章目录 1、简介2、下载和安装2.1 Git2.2 TortoiseGit 3、相关功能3.1 基本命令3.2 更新子模块命令 4、常见问题4.1 GitHub访问慢或者无法访问4.1.1 修改本地hosts映射4.1.2 通过Gitee中转fork仓库下载 4.2 OpenSSL SSL_read: Connection was reset, errno 10054 结语 1、简介…

怎么将Linux上的文件上传到github上

文章目录 1. 先在window浏览器中创建一个存储项目的仓库2. 复制你的ssh下的地址1) 生成ssh密钥 : 在Linux虚拟机的终端中,运行以下命令生成ssh密钥2)将ssh密钥添加到github账号 : 运行以下命令来获取公钥内容:3. 克隆GitHub存储库:在Linux虚拟机的终端中,导航到您想要将文件上…

redis的持久化消息队列

Redis Stream Redis Stream 是 Redis 5.0 版本新增加的数据结构。 Redis Stream 主要用于消息队列&#xff08;MQ&#xff0c;Message Queue&#xff09;&#xff0c;Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能&#xff0c;但它有个缺点就是消息无法…

OOTD | 美式复古穿搭耳机,复古轻便的头戴式耳机推荐

复古耳机更能带来年代感的复古数码产品&#xff0c;头戴式耳机就好似是时光滤镜的时髦配饰&#xff0c;不说功能实用性&#xff0c;在造型上添加就很酷。 随着时代的发展&#xff0c;时尚有了新的定义。对如今的消费者来说&#xff0c;时尚不仅是美学与个性的展现&#xff0c;…

各种机器码的本质(原码、反码、补码、移码、IEEE754格式阶码)

总述 无论使用什么格式的机器码来表示真值&#xff0c;若取一定位数n以后&#xff0c;各个比特位的排列个数是一定的&#xff0c;为 2 n 2^n 2n种排列&#xff0c;所以选择什么格式的机器码实质上选择什么映射方式来完成从这 2 n 2^n 2n种离散排列到离散的整数真值的映射&…

Open X-Embodiment Robotic Learning Datasets and RT-X Models

文章目录 简介论文链接项目链接Reference 简介 为什么机器人技术远远落后于 NLP、视觉和其他 AI 领域&#xff1f;除其他困难外&#xff0c;数据短缺是罪魁祸首。谷歌 DeepMind 联合其他机构推出了 Open X-Embodiment 数据集&#xff0c;并训练出了能力更强的 RT-X 模型。 Dee…

6-1 选择排序

#include <stdio.h>#define N 1000 int arr[N];/* 对长度为n的数组arr执行选择排序 */ void selectionSort(int arr[], int n);/* 打印长度为n的数组arr */ void printArray(int arr[], int n);void swap(int *xp, int *yp) {int temp *xp;*xp *yp;*yp temp; }int mai…

第八章 排序 二、插入排序

目录 一、算法思想 二、例子 三、代码实现 四、空间复杂度 五、时间复杂度 1、最好的情况 2、最坏的情况 六、优化&#xff08;折半插入排序&#xff09; 七、总结 一、算法思想 每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中&#xff0c;直到所…

STL-stack、queue和priority_queue的模拟实现

目录 一、容器适配器 &#xff08;一&#xff09;什么是适配器 &#xff08;二&#xff09;stack和queue的底层结构 二、Stack 三、queue 四、deque双端队列 &#xff08;一&#xff09;优点 &#xff08;二&#xff09;缺陷 五、优先级队列 &#xff08;一&#xff…

如何在终端输出颜色

效果演示: 【看 welcome to here 部分】 环境&#xff1a; Node.js 18.16.0 正文部分 我们可以通过 console.log() 在终端打印字符串。 只要在我们的字符串前面加上转义字符即可。 差不多就是下面这样的结构&#xff1a; 用代码就是&#xff1a; console.log("\x1B…

学习记忆——宫殿篇——记忆宫殿——地点桩——演讲稿定位记忆

其实在演讲的时候有很多人会遇到这样的情况&#xff0c;演讲内容准备的滚瓜烂熟&#xff0c;但是当自己在台上十分紧张的时候&#xff0c;突然忘记要说的内容。 今天在这里就用记忆宫殿的方法为大家解决这样一个问题。 注意&#xff1a;我们在运用这种记忆方法的前提是你已经对…

python笔记:pandas/geopandas DataFrame逐行遍历

在Pandas和GeoPandas中&#xff0c;可以使用几种不同的方法来遍历DataFrame的每一行 0 数据 import pandas as pddata {column1: range(1, 1001),column2: range(1001, 2001) } df pd.DataFrame(data) df 1 iterrows for index, row in df.iterrows():print(index)print(r…

SRT服务器SLS

目前互联网上的视频直播有两种&#xff0c;一种是基于RTMP协议的直播&#xff0c;这种直播方式上行推流使用RTMP协议&#xff0c;下行播放使用RTMP&#xff0c;HTTPFLV或者HLS&#xff0c;直播延时一般大于3秒&#xff0c;广泛应用秀场、游戏、赛事和事件直播&#xff0c;满足了…