├── EasyExecutor ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── yc │ │ └── easyexecutor │ │ ├── InterExecutorLog.java │ │ ├── MyThreadFactory.java │ │ ├── SafeHandler.java │ │ ├── AbsTaskExecutor.java │ │ ├── LifecycleRunnable.java │ │ ├── TaskHandlerThread.java │ │ ├── DefaultTaskExecutor.java │ │ └── DelegateTaskExecutor.java ├── proguard-rules.pro └── build.gradle ├── ThreadBusiness ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── layout │ │ │ ├── activity_thread_main.xml │ │ │ ├── activity_test.xml │ │ │ ├── activity_easy_executor.xml │ │ │ ├── activity_thread.xml │ │ │ ├── activity_poll_thread.xml │ │ │ └── activity_test_thread_count.xml │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ └── drawable │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ └── com │ │ │ └── yc │ │ │ └── ycthreadpool │ │ │ ├── LogCallback.java │ │ │ ├── ThreadMainActivity.java │ │ │ ├── App.java │ │ │ ├── EasyExecutorActivity.java │ │ │ ├── ExecutorsTestActivity.java │ │ │ ├── ThreadPollActivity.java │ │ │ ├── ThreadPoolExecutorActivity.java │ │ │ └── ThreadActivity.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── ThreadPoolLib ├── .gitignore ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── yc │ │ └── ycthreadpoollib │ │ ├── config │ │ ├── ThreadPoolType.java │ │ └── ThreadConfigs.java │ │ ├── builder │ │ ├── IThreadPoolBuilder.java │ │ ├── CachedBuilderImpl.java │ │ ├── ScheduledBuilderImpl.java │ │ ├── SingleBuilderImpl.java │ │ ├── FixedBuilderImpl.java │ │ ├── AbsThreadPoolBuilder.java │ │ └── CustomBuilderImpl.java │ │ ├── ScheduleTask.java │ │ ├── callback │ │ ├── AsyncCallback.java │ │ ├── ThreadCallback.java │ │ └── NormalCallback.java │ │ ├── deliver │ │ ├── JavaDeliver.java │ │ └── AndroidDeliver.java │ │ ├── factory │ │ └── MyThreadFactory.java │ │ ├── utils │ │ ├── DelayTaskExecutor.java │ │ └── ThreadToolUtils.java │ │ └── wrapper │ │ ├── CallableWrapper.java │ │ └── RunnableWrapper.java ├── proguard-rules.pro └── build.gradle ├── ThreadTaskLib ├── .gitignore ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── yc │ │ └── taskscheduler │ │ ├── SchedulerTask.java │ │ ├── MyThreadFactory.java │ │ ├── AbsTaskRunnable.java │ │ └── TaskScheduler.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── caches │ └── build_file_checksums.ser ├── encodings.xml ├── vcs.xml ├── compiler.xml ├── runConfigurations.xml ├── modules.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── misc.xml ├── codeStyles │ └── Project.xml └── markdown-navigator.xml ├── read ├── 08.线程导致OOM.md ├── 07.设置线程池大小.md ├── 09.线程池一些思考.md ├── 06.设置线程池原理.md ├── 05.线程池复用原理.md ├── 03.线程池深入思考.md └── 02.线程池案例测试.md ├── gradle.properties ├── .gitignore ├── yc.gradle ├── gradlew.bat └── gradlew /EasyExecutor/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /EasyExecutor/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ThreadBusiness/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /ThreadPoolLib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /ThreadTaskLib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':ThreadBusiness', ':ThreadPoolLib' 2 | include ':ThreadTaskLib' 3 | include ':EasyExecutor' 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | YCThreadPool 3 | 4 | -------------------------------------------------------------------------------- /ThreadTaskLib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TaskScheduler 3 | 4 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | YCThreadPoolLib 3 | 4 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /ThreadTaskLib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangchong211/YCThreadPool/HEAD/ThreadBusiness/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/java/com/yc/easyexecutor/InterExecutorLog.java: -------------------------------------------------------------------------------- 1 | package com.yc.easyexecutor; 2 | 3 | 4 | public interface InterExecutorLog { 5 | 6 | void info(String info); 7 | void error(String error); 8 | } 9 | -------------------------------------------------------------------------------- /read/08.线程导致OOM.md: -------------------------------------------------------------------------------- 1 | #### 目录介绍 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | 7 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /read/07.设置线程池大小.md: -------------------------------------------------------------------------------- 1 | #### 目录介绍 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ### 01.计算线程数量 11 | - 一般多线程执行的任务类型可以分为 CPU 密集型和 I/O 密集型,根据不同的任务类型,我们计算线程数的方法也不一样。 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/config/ThreadPoolType.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib.config; 2 | 3 | /** 4 | *
 5 |  *     @author yangchong
 6 |  *     blog  : https://github.com/yangchong211
 7 |  *     time  : 2019/05/17
 8 |  *     desc  : 枚举
 9 |  *     revise:
10 |  * 
11 | */ 12 | public enum ThreadPoolType { 13 | CACHED, 14 | FIXED, 15 | SCHEDULED, 16 | SINGLE, 17 | CUSTOM; 18 | 19 | private ThreadPoolType() { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/builder/IThreadPoolBuilder.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib.builder; 2 | 3 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 4 | import com.yc.ycthreadpoollib.factory.MyThreadFactory; 5 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 6 | 7 | import java.util.concurrent.ExecutorService; 8 | 9 | public interface IThreadPoolBuilder { 10 | 11 | T create(MyThreadFactory myThreadFactory); 12 | 13 | ThreadPoolType getType(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /read/09.线程池一些思考.md: -------------------------------------------------------------------------------- 1 | #### 目录介绍 2 | 3 | 4 | 5 | 6 | - 线程创建和销毁是如何损耗性能的? 7 | - 创建线程,执行run后线程自动销毁 8 | - 如何降低线程创建性能损耗? 9 | - 创建线程池,通过复用 10 | - 绘制线程的生命周期图 11 | - 创建,start,wait,sleep,join,destroy 12 | - Executors.newCachedThreadPool() 和 new ThreadPoolExecutor 有何区别 13 | - Executors 14 | - 记得大概有5种方式 15 | - ThreadPoolExecutor 16 | - 线程池类 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /read/06.设置线程池原理.md: -------------------------------------------------------------------------------- 1 | #### 目录介绍 2 | 3 | 4 | 5 | ### 01.Java线程说明 6 | - 在 HotSpot VM 的线程模型中,Java 线程被一对一映射为内核线程。 7 | - Java 在使用线程执行程序时,需要创建一个内核线程;当该 Java 线程被终止时,这个内核线程也会被回收。 8 | - 因此 Java 线程的创建与销毁将会消耗一定的计算机资源,从而增加系统的性能开销。 9 | - 除此之外,大量创建线程同样会给系统带来性能问题 10 | - 因为内存和 CPU 资源都将被线程抢占,如果处理不当,就会发生内存溢出、CPU 使用率超负荷等问题。 11 | 12 | 13 | ### 02.为何引入线程池 14 | - 为了解决上述两类问题,Java 提供了线程池概念,对于频繁创建线程的业务场景,线程池可以创建固定的线程数量,并且在操作系统底层,轻量级进程将会把这些线程映射到内核。 15 | - 线程池可以提高线程复用,又可以固定最大线程使用量,防止无限制地创建线程。 16 | - 当程序提交一个任务需要一个线程时,会去线程池中查找是否有空闲的线程,若有,则直接使用线程池中的线程工作,若没有,会去判断当前已创建的线程数量是否超过最大线程数量,如未超过,则创建新线程,如已超过,则进行排队等待或者直接抛出异常。 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /EasyExecutor/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /ThreadBusiness/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /ThreadPoolLib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /ThreadTaskLib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/java/com/yc/easyexecutor/MyThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.yc.easyexecutor; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import java.util.concurrent.atomic.AtomicLong; 5 | 6 | /** 7 | *
 8 |  *     @author yangchong
 9 |  *     email  : yangchong211@163.com
10 |  *     time   : 2019/5/11
11 |  *     desc   : ThreadFactory
12 |  *     revise :
13 |  *     GitHub : https://github.com/yangchong211/YCThreadPool
14 |  * 
15 | */ 16 | public class MyThreadFactory implements ThreadFactory { 17 | 18 | private final AtomicLong mCount = new AtomicLong(0); 19 | private final String threadName; 20 | 21 | public MyThreadFactory(String threadName){ 22 | this.threadName = threadName; 23 | } 24 | 25 | @Override 26 | public Thread newThread(Runnable r) { 27 | return new Thread(r, threadName + mCount.getAndIncrement()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json 56 | .idea/ 57 | .gradle/ 58 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/java/com/yc/ycthreadpool/LogCallback.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpool; 2 | 3 | import android.util.Log; 4 | 5 | import com.yc.ycthreadpoollib.callback.ThreadCallback; 6 | 7 | 8 | /** 9 | *
10 |  *     @author: yangchong
11 |  *     blog  : www.pedaily.cn
12 |  *     time  : 2017/08/22
13 |  *     desc  : 回调数据
14 |  *     revise:
15 |  * 
16 | */ 17 | public class LogCallback implements ThreadCallback { 18 | 19 | private final String TAG = "LogCallback"; 20 | 21 | @Override 22 | public void onError(String name, Throwable t) { 23 | Log.e(TAG, "LogCallback"+"------onError"+"-----"+name+"----"+Thread.currentThread()+"----"+t.getMessage()); 24 | } 25 | 26 | @Override 27 | public void onCompleted(String name) { 28 | Log.e(TAG, "LogCallback"+"------onCompleted"+"-----"+name+"----"+Thread.currentThread()); 29 | } 30 | 31 | @Override 32 | public void onStart(String name) { 33 | Log.e(TAG, "LogCallback"+"------onStart"+"-----"+name+"----"+Thread.currentThread()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ThreadTaskLib/src/main/java/com/yc/taskscheduler/SchedulerTask.java: -------------------------------------------------------------------------------- 1 | package com.yc.taskscheduler; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | 5 | 6 | public abstract class SchedulerTask implements Runnable { 7 | 8 | protected long startDelayMillisecond; 9 | protected long periodMillisecond; 10 | protected boolean mainThread = true; 11 | protected AtomicBoolean canceled = new AtomicBoolean(false); 12 | 13 | protected SchedulerTask(long periodMillisecond) { 14 | this.periodMillisecond = periodMillisecond; 15 | } 16 | 17 | protected SchedulerTask(long periodMillisecond,boolean mainThread) { 18 | this.periodMillisecond = periodMillisecond; 19 | this.mainThread = mainThread; 20 | } 21 | 22 | protected SchedulerTask(long periodMillisecond,boolean mainThread,long startDelayMillisecond) { 23 | this.periodMillisecond = periodMillisecond; 24 | this.mainThread = mainThread; 25 | this.startDelayMillisecond = startDelayMillisecond; 26 | } 27 | 28 | public abstract void onSchedule(); 29 | 30 | @Override 31 | public void run() { 32 | if(!canceled.get()) { 33 | onSchedule(); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /EasyExecutor/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | apply from: rootProject.projectDir.absolutePath + "/yc.gradle" 5 | apply plugin: 'com.github.dcendents.android-maven' 6 | 7 | android { 8 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 9 | //buildToolsVersion rootProject.ext.android["buildToolsVersion"] 10 | defaultConfig { 11 | minSdkVersion rootProject.ext.android["minSdkVersion"] 12 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 13 | versionCode rootProject.ext.android["versionCode"] 14 | versionName rootProject.ext.android["versionName"] 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(dir: 'libs', include: ['*.jar']) 31 | implementation(rootProject.ext.dependencies["appcompat"]) 32 | implementation(rootProject.ext.dependencies["annotation"]) 33 | } -------------------------------------------------------------------------------- /ThreadPoolLib/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | apply from: rootProject.projectDir.absolutePath + "/yc.gradle" 5 | apply plugin: 'com.github.dcendents.android-maven' 6 | 7 | android { 8 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 9 | //buildToolsVersion rootProject.ext.android["buildToolsVersion"] 10 | defaultConfig { 11 | minSdkVersion rootProject.ext.android["minSdkVersion"] 12 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 13 | versionCode rootProject.ext.android["versionCode"] 14 | versionName rootProject.ext.android["versionName"] 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(dir: 'libs', include: ['*.jar']) 31 | implementation(rootProject.ext.dependencies["appcompat"]) 32 | implementation(rootProject.ext.dependencies["annotation"]) 33 | } -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/ScheduleTask.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib; 2 | 3 | 4 | import androidx.annotation.NonNull; 5 | 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.ThreadFactory; 9 | 10 | public final class ScheduleTask { 11 | 12 | private final ExecutorService mExecutors; 13 | 14 | private ScheduleTask() { 15 | ThreadFactory threadFactory = new ThreadFactory() { 16 | @Override 17 | public Thread newThread(@NonNull Runnable runnable) { 18 | //创建一个线程 19 | return new Thread(runnable, "ScheduleTask"); 20 | } 21 | }; 22 | this.mExecutors = Executors.newSingleThreadExecutor(threadFactory); 23 | } 24 | 25 | public static ScheduleTask getInstance() { 26 | //单例模式 27 | return ScheduleTask.Holder.INSTANCE; 28 | } 29 | 30 | public void schedule(Runnable runnable) { 31 | //提交一个事务,runnable 32 | this.mExecutors.execute(runnable); 33 | } 34 | 35 | private static class Holder { 36 | 37 | private static final ScheduleTask INSTANCE = new ScheduleTask(); 38 | 39 | private Holder() { 40 | 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/builder/CachedBuilderImpl.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib.builder; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 7 | import com.yc.ycthreadpoollib.factory.MyThreadFactory; 8 | 9 | /** 10 | *
11 |  *     @author yangchong
12 |  *     blog  : https://github.com/yangchong211
13 |  *     time  : 2019/05/17
14 |  *     desc  : 缓存线程池
15 |  *     revise: 它是一个数量无限多的线程池,都是非核心线程,适合执行大量耗时小的任务
16 |  * 
17 | */ 18 | public class CachedBuilderImpl extends AbsThreadPoolBuilder { 19 | 20 | public CachedBuilderImpl() { 21 | 22 | } 23 | 24 | public ExecutorService create(MyThreadFactory myThreadFactory) { 25 | //相当于这个 26 | /*return new ThreadPoolExecutor( 27 | 0, Integer.MAX_VALUE, 28 | 60L, TimeUnit.SECONDS, 29 | new SynchronousQueue(), 30 | myThreadFactory);*/ 31 | //它是一个数量无限多的线程池,都是非核心线程,适合执行大量耗时小的任务 32 | return Executors.newCachedThreadPool(myThreadFactory); 33 | } 34 | 35 | @Override 36 | public ThreadPoolType getType() { 37 | return ThreadPoolType.CACHED; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ThreadTaskLib/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | apply from: rootProject.projectDir.absolutePath + "/yc.gradle" 5 | apply plugin: 'com.github.dcendents.android-maven' 6 | 7 | android { 8 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 9 | //buildToolsVersion rootProject.ext.android["buildToolsVersion"] 10 | defaultConfig { 11 | minSdkVersion rootProject.ext.android["minSdkVersion"] 12 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 13 | versionCode rootProject.ext.android["versionCode"] 14 | versionName rootProject.ext.android["versionName"] 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(dir: 'libs', include: ['*.jar']) 31 | implementation(rootProject.ext.dependencies["appcompat"]) 32 | implementation(rootProject.ext.dependencies["annotation"]) 33 | implementation project(path: ':EasyExecutor') 34 | } -------------------------------------------------------------------------------- /ThreadBusiness/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/java/com/yc/easyexecutor/SafeHandler.java: -------------------------------------------------------------------------------- 1 | package com.yc.easyexecutor; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | import android.util.Log; 7 | 8 | import androidx.annotation.Nullable; 9 | 10 | /** 11 | *
12 |  *     @author yangchong
13 |  *     email  : yangchong211@163.com
14 |  *     time   : 2019/5/11
15 |  *     desc   : 安全版本的handler
16 |  *     revise :
17 |  *     GitHub : https://github.com/yangchong211/YCThreadPool
18 |  * 
19 | */ 20 | public final class SafeHandler extends Handler { 21 | 22 | private static final String TAG = "SafeHandler"; 23 | 24 | public SafeHandler(Looper looper) { 25 | super(looper); 26 | } 27 | 28 | public SafeHandler() { 29 | //默认是主线程 30 | super(Looper.getMainLooper()); 31 | } 32 | 33 | @Override 34 | public void dispatchMessage(@Nullable Message msg) { 35 | if (msg == null){ 36 | Log.d(TAG, "msg is null , return"); 37 | return; 38 | } 39 | try { 40 | super.dispatchMessage(msg); 41 | } catch (Exception e) { 42 | Log.d(TAG, "dispatchMessage Exception " + msg + " , " + e); 43 | } catch (Error error) { 44 | Log.d(TAG, "dispatchMessage error " + msg + " , " + error); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/builder/ScheduledBuilderImpl.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib.builder; 2 | 3 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 4 | import com.yc.ycthreadpoollib.factory.MyThreadFactory; 5 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 6 | 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.ScheduledExecutorService; 10 | 11 | /** 12 | *
13 |  *     @author yangchong
14 |  *     blog  : https://github.com/yangchong211
15 |  *     time  : 2019/05/17
16 |  *     desc  : SingleBuilder
17 |  *     revise: 有数量固定的核心线程,且有数量无限多的非核心线程,适合用于执行定时任务和固定周期的重复任务
18 |  * 
19 | */ 20 | public class ScheduledBuilderImpl extends AbsThreadPoolBuilder { 21 | 22 | private int mSize = 1; 23 | 24 | public ScheduledBuilderImpl() { 25 | } 26 | 27 | public ScheduledExecutorService create(MyThreadFactory myThreadFactory) { 28 | //有数量固定的核心线程,且有数量无限多的非核心线程,适合用于执行定时任务和固定周期的重复任务 29 | return Executors.newScheduledThreadPool(this.mSize, myThreadFactory); 30 | } 31 | 32 | public ThreadPoolType getType() { 33 | return ThreadPoolType.SCHEDULED; 34 | } 35 | 36 | public ScheduledBuilderImpl setSize(int size) { 37 | this.mSize = size; 38 | return this; 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/builder/SingleBuilderImpl.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib.builder; 2 | 3 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 4 | import com.yc.ycthreadpoollib.factory.MyThreadFactory; 5 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 6 | 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | 10 | /** 11 | *
12 |  *     @author yangchong
13 |  *     blog  : https://github.com/yangchong211
14 |  *     time  : 2019/05/17
15 |  *     desc  : SingleBuilder
16 |  *     revise: 内部只有一个核心线程,所有任务进来都要排队按顺序执行
17 |  * 
18 | */ 19 | public class SingleBuilderImpl extends AbsThreadPoolBuilder { 20 | 21 | public SingleBuilderImpl() { 22 | 23 | } 24 | 25 | public ExecutorService create(MyThreadFactory myThreadFactory) { 26 | //相当于这个 27 | /*return new FinalizableDelegatedExecutorService 28 | (new ThreadPoolExecutor(1, 1, 29 | 0L, TimeUnit.MILLISECONDS, 30 | new LinkedBlockingQueue(), 31 | myThreadFactory));*/ 32 | //内部只有一个核心线程,所有任务进来都要排队按顺序执行 33 | return Executors.newSingleThreadExecutor(myThreadFactory); 34 | } 35 | 36 | public ThreadPoolType getType() { 37 | return ThreadPoolType.SINGLE; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/callback/AsyncCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.yc.ycthreadpoollib.callback; 17 | 18 | 19 | 20 | /** 21 | *
22 |  *     @author yangchong
23 |  *     blog  : https://github.com/yangchong211
24 |  *     time  : 2017/08/22
25 |  *     desc  : 异步callback回调接口
26 |  *     revise:
27 |  * 
28 | */ 29 | public interface AsyncCallback { 30 | 31 | /** 32 | * 成功时调用 33 | * @param t 泛型 34 | */ 35 | void onSuccess(T t); 36 | 37 | /** 38 | * 异常时调用 39 | * @param t 异常 40 | */ 41 | void onFailed(Throwable t); 42 | 43 | 44 | /** 45 | * 通知用户任务开始运行 46 | * @param threadName 正在运行线程的名字 47 | */ 48 | void onStart(String threadName); 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/builder/FixedBuilderImpl.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib.builder; 2 | 3 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 4 | import com.yc.ycthreadpoollib.factory.MyThreadFactory; 5 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 6 | 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | 10 | /** 11 | *
12 |  *     @author yangchong
13 |  *     blog  : https://github.com/yangchong211
14 |  *     time  : 2019/05/17
15 |  *     desc  : SingleBuilder
16 |  *     revise: 线程数量固定的线程池,全部为核心线程,响应较快,不用担心线程会被回收。
17 |  * 
18 | */ 19 | public class FixedBuilderImpl extends AbsThreadPoolBuilder { 20 | 21 | private int mSize = 1; 22 | 23 | public FixedBuilderImpl() { 24 | } 25 | 26 | public ExecutorService create(MyThreadFactory myThreadFactory) { 27 | // //相当于这个 28 | /*return new ThreadPoolExecutor( 29 | this.mSize, this.mSize, 30 | 0L, TimeUnit.MILLISECONDS, 31 | new LinkedBlockingQueue(), 32 | myThreadFactory);*/ 33 | //线程数量固定的线程池,全部为核心线程,响应较快,不用担心线程会被回收。 34 | return Executors.newFixedThreadPool(this.mSize, myThreadFactory); 35 | } 36 | 37 | public ThreadPoolType getType() { 38 | return ThreadPoolType.FIXED; 39 | } 40 | 41 | public FixedBuilderImpl setSize(int size) { 42 | this.mSize = size; 43 | return this; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/callback/ThreadCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.yc.ycthreadpoollib.callback; 17 | 18 | 19 | /** 20 | *
21 |  *     @author yangchong
22 |  *     blog  : https://github.com/yangchong211
23 |  *     time  :
24 |  *     desc  : 一个回调接口,用于通知用户任务的状态回调委托类
25 |  *             线程的名字可以自定义
26 |  *     revise:
27 |  * 
28 | */ 29 | public interface ThreadCallback { 30 | 31 | /** 32 | * 当线程发生错误时,将调用此方法。 33 | * @param threadName 正在运行线程的名字 34 | * @param t 异常 35 | */ 36 | void onError(String threadName, Throwable t); 37 | 38 | /** 39 | * 通知用户知道它已经完成 40 | * @param threadName 正在运行线程的名字 41 | */ 42 | void onCompleted(String threadName); 43 | 44 | /** 45 | * 通知用户任务开始运行 46 | * @param threadName 正在运行线程的名字 47 | */ 48 | void onStart(String threadName); 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/deliver/JavaDeliver.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.yc.ycthreadpoollib.deliver; 18 | 19 | import androidx.annotation.Nullable; 20 | 21 | import java.util.concurrent.Executor; 22 | 23 | 24 | /** 25 | *
26 |  *     @author yangchong
27 |  *     blog  : https://github.com/yangchong211
28 |  *     time  :
29 |  *     desc  : 默认情况下,用于Java平台的交付。
30 |  *     revise:
31 |  * 
32 | */ 33 | public final class JavaDeliver implements Executor { 34 | 35 | private static JavaDeliver instance = new JavaDeliver(); 36 | 37 | /** 38 | * 使用单利模式获取对象 39 | * @return JavaDeliver对象 40 | */ 41 | public static JavaDeliver getInstance() { 42 | return instance; 43 | } 44 | 45 | /** 46 | * 注意增加非空判断 47 | * @param runnable runnable 48 | */ 49 | @Override 50 | public void execute(@Nullable Runnable runnable) { 51 | if (runnable!=null){ 52 | runnable.run(); 53 | } 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/layout/activity_thread_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 25 | 26 | 34 | 35 | 43 | 44 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/config/ThreadConfigs.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.yc.ycthreadpoollib.config; 18 | 19 | import java.util.concurrent.Executor; 20 | 21 | import com.yc.ycthreadpoollib.callback.AsyncCallback; 22 | import com.yc.ycthreadpoollib.callback.ThreadCallback; 23 | 24 | 25 | /** 26 | *
27 |  *     @author yangchong
28 |  *     blog  : https://github.com/yangchong211
29 |  *     time  : 2017/08/22
30 |  *     desc  : 存储当前任务的某些配置
31 |  *     revise:
32 |  * 
33 | */ 34 | 35 | public final class ThreadConfigs { 36 | 37 | /** 38 | * 线程的名称 39 | * 通过setName方法设置 40 | */ 41 | public String name; 42 | /** 43 | * 线程执行延迟的时间 44 | * 通过setDelay方法设置 45 | */ 46 | public long delay; 47 | /** 48 | * 线程执行者 49 | * JAVA或者ANDROID 50 | */ 51 | public Executor deliver; 52 | /** 53 | * 用户任务的状态回调callback 54 | */ 55 | public ThreadCallback callback; 56 | /** 57 | * 异步callback回调callback 58 | */ 59 | public AsyncCallback asyncCallback; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ThreadBusiness/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply from: rootProject.projectDir.absolutePath + "/yc.gradle" 3 | 4 | android { 5 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 6 | buildToolsVersion rootProject.ext.android["buildToolsVersion"] 7 | defaultConfig { 8 | applicationId "com.yc.ycthreadpool" 9 | minSdkVersion rootProject.ext.android["minSdkVersion"] 10 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 11 | versionCode rootProject.ext.android["versionCode"] 12 | versionName rootProject.ext.android["versionName"] 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(include: ['*.jar'], dir: 'libs') 28 | implementation(rootProject.ext.dependencies["appcompat"]) 29 | implementation(rootProject.ext.dependencies["annotation"]) 30 | //通用日志输出库 31 | implementation 'com.github.yangchong211.YCCommonLib:AppLogLib:1.3.0' 32 | implementation 'com.github.yangchong211.YCDialog:ToastUtils:3.6.9' 33 | // implementation project(':ThreadPoolLib') 34 | // implementation project(':ThreadTaskLib') 35 | // implementation project(':EasyExecutor') 36 | implementation 'com.github.yangchong211.YCThreadPool:ThreadPoolLib:1.3.8' 37 | implementation 'com.github.yangchong211.YCThreadPool:ThreadTaskLib:1.3.8' 38 | implementation 'com.github.yangchong211.YCThreadPool:EasyExecutor:1.3.8' 39 | } 40 | -------------------------------------------------------------------------------- /ThreadTaskLib/src/main/java/com/yc/taskscheduler/MyThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.yc.taskscheduler; 2 | 3 | import android.os.Process; 4 | 5 | import java.util.concurrent.ThreadFactory; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | 9 | class MyThreadFactory { 10 | 11 | static final class BackgroundRunnable implements Runnable { 12 | private final Runnable runnable; 13 | 14 | BackgroundRunnable(Runnable runnable) { 15 | this.runnable = runnable; 16 | } 17 | 18 | @Override 19 | public void run() { 20 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 21 | runnable.run(); 22 | } 23 | } 24 | 25 | static final ThreadFactory TASK_SCHEDULER_FACTORY = new ThreadFactory() { 26 | private final AtomicInteger mCount = new AtomicInteger(1); 27 | 28 | @Override 29 | public Thread newThread(Runnable r) { 30 | return new Thread(new BackgroundRunnable(r), "TaskScheduler #" + mCount.getAndIncrement()); 31 | } 32 | }; 33 | 34 | static final ThreadFactory TIME_OUT_THREAD_FACTORY = new ThreadFactory() { 35 | private final AtomicInteger mCount = new AtomicInteger(1); 36 | 37 | @Override 38 | public Thread newThread(Runnable r) { 39 | return new Thread(new BackgroundRunnable(r), "TaskScheduler timeoutThread #" + mCount.getAndIncrement()); 40 | } 41 | }; 42 | 43 | static final ThreadFactory SCHEDULER_THREAD_FACTORY = new ThreadFactory() { 44 | 45 | private final AtomicInteger mCount = new AtomicInteger(1); 46 | 47 | @Override 48 | public Thread newThread(Runnable r) { 49 | return new Thread(new BackgroundRunnable(r), "TaskScheduler scheduler #" + mCount.getAndIncrement()); 50 | } 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/builder/AbsThreadPoolBuilder.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib.builder; 2 | 3 | 4 | import com.yc.ycthreadpoollib.factory.MyThreadFactory; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ExecutorService; 9 | 10 | /** 11 | *
12 |  *     @author yangchong
13 |  *     blog  : https://github.com/yangchong211
14 |  *     time  : 2019/05/17
15 |  *     desc  : 抽象类
16 |  *     revise:
17 |  * 
18 | */ 19 | public abstract class AbsThreadPoolBuilder implements IThreadPoolBuilder{ 20 | 21 | /** 22 | * ConcurrentHashMap 多线程下数据安全 23 | */ 24 | protected static Map mThreadPoolMap = new ConcurrentHashMap<>(); 25 | protected ExecutorService mExecutorService = null; 26 | protected String mPoolName = "default"; 27 | 28 | public AbsThreadPoolBuilder() { 29 | } 30 | 31 | 32 | public ExecutorService builder(MyThreadFactory myThreadFactory) { 33 | String globalPoolName = "YC_" + this.getType() + "_" + this.mPoolName; 34 | if (mThreadPoolMap.get(globalPoolName) != null) { 35 | this.mExecutorService = (ExecutorService)mThreadPoolMap.get(globalPoolName); 36 | } else { 37 | this.mExecutorService = this.create(myThreadFactory); 38 | mThreadPoolMap.put(globalPoolName, this.mExecutorService); 39 | } 40 | return this.mExecutorService; 41 | } 42 | 43 | public ExecutorService getExecutorService() { 44 | return this.mExecutorService; 45 | } 46 | 47 | public AbsThreadPoolBuilder poolName(String poolName) { 48 | if (poolName != null && poolName.length() > 0) { 49 | this.mPoolName = poolName; 50 | } 51 | return this; 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/factory/MyThreadFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.yc.ycthreadpoollib.factory; 18 | 19 | 20 | import androidx.annotation.NonNull; 21 | 22 | import java.util.concurrent.ThreadFactory; 23 | 24 | 25 | /** 26 | *
27 |  *     @author yangchong
28 |  *     blog  : https://github.com/yangchong211
29 |  *     time  : 2017/08/22
30 |  *     desc  : 默认Thread工厂
31 |  *     revise:
32 |  * 
33 | */ 34 | public class MyThreadFactory implements ThreadFactory { 35 | 36 | /** 37 | * ThreadFactory 38 | * ThreadFactory是一个接口,里面只有一个newThread方法 39 | * 线程工厂,为线程池提供新线程的创建 40 | */ 41 | 42 | private int priority; 43 | 44 | /** 45 | * 构造方法,默认为优先级是:Thread.NORM_PRIORITY 46 | */ 47 | public MyThreadFactory() { 48 | this.priority = Thread.NORM_PRIORITY; 49 | } 50 | 51 | /** 52 | * 构造方法 53 | * @param priority 优先级 54 | */ 55 | public MyThreadFactory(int priority) { 56 | this.priority = priority; 57 | } 58 | 59 | 60 | @Override 61 | public Thread newThread(@NonNull Runnable runnable) { 62 | // 创建线程 63 | Thread thread = new Thread(runnable); 64 | // 设置线程优先级 65 | thread.setPriority(priority); 66 | return thread; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /yc.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | 3 | android = [ 4 | compileSdkVersion: 29, 5 | buildToolsVersion: "29.0.0", 6 | minSdkVersion : 17, 7 | targetSdkVersion : 29, 8 | versionCode : 22, 9 | versionName : "1.8.2" //必须是int或者float,否则影响线上升级 10 | ] 11 | 12 | buildToolsVersion = "29.0.0" 13 | minSdkVersion = 17 14 | targetSdkVersion = 29 15 | compileSdkVersion = 29 16 | versionCode = 22 17 | versionName = "1.8.2" 18 | 19 | //AndroidX系列 20 | appcompatVersion = '1.2.0' 21 | annotationVersion = '1.2.0' 22 | cardviewVersion = '1.0.0' 23 | mediaVersion = '1.0.1' 24 | swiperefreshlayoutVersion = '1.0.0' 25 | materialVersion = '1.0.0-rc01' 26 | coordinatorlayoutVersion = '1.0.0' 27 | constraintlayoutVersion = '1.1.3' 28 | recyclerviewVersion = '1.0.0' 29 | multidexVersion = '1.0.2' 30 | 31 | dependencies = [ 32 | //AndroidX系列 33 | appcompat : "androidx.appcompat:appcompat:${appcompatVersion}", 34 | annotation : "androidx.annotation:annotation:${annotationVersion}", 35 | constraintlayout : "androidx.constraintlayout:constraintlayout:${constraintlayoutVersion}", 36 | coordinatorlayout : "androidx.coordinatorlayout:coordinatorlayout:${coordinatorlayoutVersion}", 37 | cardview : "androidx.cardview:cardview:${cardviewVersion}", 38 | recyclerview : "androidx.recyclerview:recyclerview:${recyclerviewVersion}", 39 | media : "androidx.media:media:${mediaVersion}", 40 | material : "com.google.android.material:material:${materialVersion}", 41 | swiperefreshlayout : "androidx.swiperefreshlayout:swiperefreshlayout:${swiperefreshlayoutVersion}", 42 | "multidex" : "com.android.support:multidex:$multidexVersion", 43 | ] 44 | 45 | } 46 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/layout/activity_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 17 | 18 | 19 | 27 | 28 | 36 | 37 | 45 | 46 | 47 | 55 | 56 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/layout/activity_easy_executor.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 25 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/deliver/AndroidDeliver.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.yc.ycthreadpoollib.deliver; 18 | 19 | import android.os.Handler; 20 | import android.os.Looper; 21 | 22 | import androidx.annotation.Nullable; 23 | 24 | import java.util.concurrent.Executor; 25 | 26 | 27 | /** 28 | *
29 |  *     @author yangchong
30 |  *     blog  : https://github.com/yangchong211
31 |  *     time  : 2017/08/22
32 |  *     desc  :
33 |  *     revise:
34 |  * 
35 | */ 36 | public final class AndroidDeliver implements Executor { 37 | 38 | private static AndroidDeliver instance = new AndroidDeliver(); 39 | private Handler main = new Handler(Looper.getMainLooper()); 40 | 41 | public static AndroidDeliver getInstance() { 42 | return instance; 43 | } 44 | 45 | @Override 46 | public void execute(@Nullable final Runnable runnable) { 47 | //返回应用程序的looper,它位于应用程序的主线程中。 48 | Looper mainLooper = Looper.getMainLooper(); 49 | //如果当前looper就是当前主线程,那么调用run后不再执行下面的语句 50 | if (Looper.myLooper() == mainLooper && runnable!=null) { 51 | runnable.run(); 52 | return; 53 | } 54 | //开启子线程 55 | main.post(new Runnable() { 56 | @Override 57 | public void run() { 58 | //注意:这里需要增加非空判断 59 | if(runnable!=null){ 60 | runnable.run(); 61 | } 62 | } 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/java/com/yc/easyexecutor/AbsTaskExecutor.java: -------------------------------------------------------------------------------- 1 | package com.yc.easyexecutor; 2 | 3 | import android.os.Handler; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.RestrictTo; 7 | 8 | /** 9 | *
10 |  *     @author yangchong
11 |  *     email  : yangchong211@163.com
12 |  *     time   : 2019/5/11
13 |  *     desc   : 抽象task任务类
14 |  *     revise :
15 |  *     GitHub : https://github.com/yangchong211/YCThreadPool
16 |  * 
17 | */ 18 | public abstract class AbsTaskExecutor { 19 | 20 | /** 21 | * 核心任务的线程池,执行任务 22 | * 23 | * @param runnable 任务 24 | */ 25 | public abstract void executeOnCore(@NonNull Runnable runnable); 26 | 27 | /** 28 | * IO 密集型任务的线程池,执行任务 29 | * 30 | * @param runnable 任务 31 | */ 32 | public abstract void executeOnDiskIO(@NonNull Runnable runnable); 33 | 34 | /** 35 | * CPU 密集型任务的线程池,执行任务 36 | * 37 | * @param runnable 任务 38 | */ 39 | public abstract void executeOnCpu(@NonNull Runnable runnable); 40 | 41 | /** 42 | * UI主线程共有handler对象,执行任务 43 | * 44 | * @param runnable 任务 45 | */ 46 | public abstract void postToMainThread(@NonNull Runnable runnable); 47 | 48 | /** 49 | * 获取UI主线程共有handler对象 50 | * 51 | * @return handler对象 52 | */ 53 | public abstract Handler getMainHandler(); 54 | 55 | /** 56 | * 配合HandlerThread使用的handler【handlerThread具有自己的looper】,一般用来执行大量任务,执行任务 57 | * 一般用于在一个后台线程执行同一种任务,避免线程安全问题。如数据库,文件操作,轮训操作等 58 | * @param runnable 任务 59 | */ 60 | public abstract void postIoHandler(@NonNull Runnable runnable); 61 | 62 | /** 63 | * UI主线程共有handler对象,执行任务 64 | * 65 | * @param runnable 任务 66 | */ 67 | public void executeOnMainThread(@NonNull Runnable runnable) { 68 | if (isMainThread()) { 69 | runnable.run(); 70 | } else { 71 | postToMainThread(runnable); 72 | } 73 | } 74 | 75 | /** 76 | * 判断是否是主线程 77 | * 78 | * @return true 79 | */ 80 | public abstract boolean isMainThread(); 81 | } 82 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/java/com/yc/ycthreadpool/ThreadMainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpool; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | import android.util.Log; 9 | import android.view.View; 10 | 11 | import com.yc.ycthreadpoollib.PoolThread; 12 | import com.yc.ycthreadpoollib.callback.AsyncCallback; 13 | import com.yc.ycthreadpoollib.callback.ThreadCallback; 14 | import com.yc.ycthreadpoollib.deliver.AndroidDeliver; 15 | 16 | import java.util.concurrent.Callable; 17 | import java.util.concurrent.ExecutionException; 18 | import java.util.concurrent.Future; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | 22 | public class ThreadMainActivity extends AppCompatActivity implements View.OnClickListener { 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_thread_main); 28 | findViewById(R.id.tv_easy).setOnClickListener(this); 29 | findViewById(R.id.tv_poll).setOnClickListener(this); 30 | findViewById(R.id.tv_executors).setOnClickListener(this); 31 | findViewById(R.id.tv_thread).setOnClickListener(this); 32 | 33 | 34 | // 计算可使用的最大内存 35 | final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 36 | // 取四分之一的可用内存作为缓存 37 | final int cacheSize = maxMemory / 4; 38 | } 39 | 40 | @Override 41 | public void onClick(View v) { 42 | switch (v.getId()){ 43 | case R.id.tv_easy: 44 | EasyExecutorActivity.startActivity(this); 45 | break; 46 | case R.id.tv_poll: 47 | ThreadPollActivity.startActivity(this); 48 | break; 49 | case R.id.tv_executors: 50 | startActivity(new Intent(this, ExecutorsTestActivity.class)); 51 | break; 52 | case R.id.tv_thread: 53 | startActivity(new Intent(this,ThreadActivity.class)); 54 | break; 55 | default: 56 | break; 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/java/com/yc/easyexecutor/LifecycleRunnable.java: -------------------------------------------------------------------------------- 1 | package com.yc.easyexecutor; 2 | 3 | 4 | import android.os.Handler; 5 | 6 | import androidx.lifecycle.GenericLifecycleObserver; 7 | import androidx.lifecycle.Lifecycle; 8 | import androidx.lifecycle.LifecycleOwner; 9 | 10 | 11 | public class LifecycleRunnable implements Runnable { 12 | 13 | private Runnable mOriginRunnable; 14 | private LifecycleOwner mLifecycleOwner; 15 | private GenericLifecycleObserver mLifecycleObserver; 16 | 17 | 18 | LifecycleRunnable(LifecycleOwner lifecycleOwner, final Handler handler, 19 | final Lifecycle.Event targetEvent, 20 | final Runnable originRunnable) { 21 | if(originRunnable == null || lifecycleOwner == null) { 22 | return; 23 | } 24 | this.mLifecycleOwner = lifecycleOwner; 25 | this.mOriginRunnable = originRunnable; 26 | mLifecycleObserver = new GenericLifecycleObserver() { 27 | @Override 28 | public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { 29 | if(event == targetEvent) { 30 | if(mLifecycleOwner!=null ) { 31 | mLifecycleOwner.getLifecycle().removeObserver(this); 32 | } 33 | handler.removeCallbacks(LifecycleRunnable.this); 34 | } 35 | } 36 | }; 37 | if(DelegateTaskExecutor.getInstance().isMainThread()) { 38 | mLifecycleOwner.getLifecycle().addObserver(mLifecycleObserver); 39 | }else { 40 | DelegateTaskExecutor.getInstance().postToMainThread(new Runnable() { 41 | @Override 42 | public void run() { 43 | mLifecycleOwner.getLifecycle().addObserver(mLifecycleObserver); 44 | } 45 | }); 46 | } 47 | } 48 | 49 | 50 | @Override 51 | public void run() { 52 | if(mOriginRunnable!=null && mLifecycleOwner!=null) { 53 | mOriginRunnable.run(); 54 | mLifecycleOwner.getLifecycle().removeObserver(mLifecycleObserver); 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/java/com/yc/easyexecutor/TaskHandlerThread.java: -------------------------------------------------------------------------------- 1 | 2 | package com.yc.easyexecutor; 3 | 4 | import android.os.Handler; 5 | import android.os.HandlerThread; 6 | import android.os.Looper; 7 | 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | 11 | /** 12 | *
13 |  *     @author yangchong
14 |  *     email  : yangchong211@163.com
15 |  *     time   : 2019/5/11
16 |  *     desc   : 抽象task任务类
17 |  *     revise :
18 |  *     GitHub : https://github.com/yangchong211/YCThreadPool
19 |  * 
20 | */ 21 | public final class TaskHandlerThread extends HandlerThread { 22 | 23 | private static final ConcurrentHashMap S_HASH_MAP 24 | = new ConcurrentHashMap<>(); 25 | 26 | private TaskHandlerThread(String threadName) { 27 | super(threadName, android.os.Process.THREAD_PRIORITY_BACKGROUND); 28 | } 29 | 30 | private static void ensureThreadLocked(String threadName) { 31 | TaskHandlerThread taskHandlerThread = S_HASH_MAP.get(threadName); 32 | if (taskHandlerThread == null) { 33 | synchronized (TaskHandlerThread.class) { 34 | //创建一个HandlerThread对象 35 | taskHandlerThread = new TaskHandlerThread(threadName); 36 | taskHandlerThread.start(); 37 | //存储数据 38 | S_HASH_MAP.put(threadName,taskHandlerThread); 39 | } 40 | } 41 | } 42 | 43 | public static TaskHandlerThread get(String threadName) { 44 | synchronized (TaskHandlerThread.class) { 45 | ensureThreadLocked(threadName); 46 | return S_HASH_MAP.get(threadName); 47 | } 48 | } 49 | 50 | public Handler getHandler(String threadName) { 51 | TaskHandlerThread taskHandlerThread = S_HASH_MAP.get(threadName); 52 | Handler handler; 53 | if (taskHandlerThread != null){ 54 | //获取该thread的独有looper对象 55 | final Looper looper = taskHandlerThread.getLooper(); 56 | handler = new SafeHandler(looper); 57 | } else { 58 | //普通的handler 59 | handler = new SafeHandler(); 60 | } 61 | return handler; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/layout/activity_thread.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 17 | 18 | 19 | 27 | 28 | 29 | 37 | 38 | 39 | 47 | 48 | 49 | 57 | 58 | 66 | 67 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/layout/activity_poll_thread.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/java/com/yc/ycthreadpool/App.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpool; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.yc.apploglib.config.AppLogConfig; 7 | import com.yc.apploglib.config.AppLogFactory; 8 | import com.yc.toolutils.file.AppFileUtils; 9 | import com.yc.ycthreadpoollib.PoolThread; 10 | import com.yc.ycthreadpoollib.ScheduleTask; 11 | 12 | public class App extends Application{ 13 | 14 | 15 | private static App instance; 16 | private PoolThread executor; 17 | 18 | public static synchronized App getInstance() { 19 | if (null == instance) { 20 | instance = new App(); 21 | } 22 | return instance; 23 | } 24 | 25 | 26 | public App(){} 27 | 28 | 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | instance = this; 33 | //初始化线程池管理器 34 | initThreadPool(); 35 | ScheduleTask.getInstance().schedule(new Runnable() { 36 | @Override 37 | public void run() { 38 | //做一些耗时任务 39 | 40 | } 41 | }); 42 | initLog(); 43 | } 44 | 45 | private void initLog() { 46 | String ycLogPath = AppFileUtils.getCacheFilePath(this, "ycLog"); 47 | AppLogConfig config = new AppLogConfig.Builder() 48 | //设置日志tag总的标签 49 | .setLogTag("yc") 50 | //是否将log日志写到文件 51 | .isWriteFile(true) 52 | //是否是debug 53 | .enableDbgLog(true) 54 | //设置日志最小级别 55 | .minLogLevel(Log.VERBOSE) 56 | //设置输出日志到file文件的路径。前提是将log日志写入到文件设置成true 57 | .setFilePath(ycLogPath) 58 | .build(); 59 | //配置 60 | AppLogFactory.init(config); 61 | } 62 | 63 | 64 | /** 65 | * 初始化线程池管理器 66 | */ 67 | private void initThreadPool() { 68 | // 创建一个独立的实例进行使用 69 | executor = PoolThread.ThreadBuilder 70 | .createFixed(5) 71 | .setPriority(Thread.MAX_PRIORITY) 72 | .setCallback(new LogCallback()) 73 | .build(); 74 | } 75 | 76 | /** 77 | * 获取线程池管理器对象,统一的管理器维护所有的线程池 78 | * @return executor对象 79 | */ 80 | public PoolThread getExecutor(){ 81 | if(executor ==null){ 82 | executor = PoolThread.ThreadBuilder 83 | .createFixed(5) 84 | .setPriority(Thread.MAX_PRIORITY) 85 | .setCallback(new LogCallback()) 86 | .build(); 87 | } 88 | return executor; 89 | } 90 | 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/layout/activity_test_thread_count.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 25 | 26 | 34 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 71 | 72 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/utils/DelayTaskExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.yc.ycthreadpoollib.utils; 18 | 19 | import java.util.concurrent.ExecutorService; 20 | import java.util.concurrent.Executors; 21 | import java.util.concurrent.ScheduledExecutorService; 22 | import java.util.concurrent.ThreadFactory; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | /** 26 | *
27 |  *     @author: yangchong
28 |  *     blog  : https://github.com/yangchong211
29 |  *     time  : 2017/08/22
30 |  *     desc  : 使用核心线程池启动延迟任务的类
31 |  *     revise:
32 |  * 
33 | */ 34 | @SuppressWarnings("ALL") 35 | public final class DelayTaskExecutor { 36 | 37 | private volatile ScheduledExecutorService dispatcher; 38 | 39 | /** 40 | * 单利模式,创建线程池对象 41 | */ 42 | private static DelayTaskExecutor instance = new DelayTaskExecutor(); 43 | 44 | private DelayTaskExecutor() { 45 | dispatcher = Executors.newScheduledThreadPool(1, new ThreadFactory() { 46 | @Override 47 | public Thread newThread(Runnable runnable) { 48 | Thread thread = new Thread(runnable); 49 | thread.setName("Yc_Delay-Task-Dispatcher"); 50 | thread.setPriority(Thread.MAX_PRIORITY); 51 | return thread; 52 | } 53 | }); 54 | } 55 | 56 | public static synchronized DelayTaskExecutor get() { 57 | return instance; 58 | } 59 | 60 | /** 61 | * 启动 62 | * @param delay 延迟执行的时间,注意默认单位是TimeUnit.MILLISECONDS 63 | * @param pool pool线程池 64 | * @param task runnable 65 | */ 66 | public void postDelay(long delay, final ExecutorService pool, final Runnable task) { 67 | if (delay == 0) { 68 | //如果时间是0,那么普通开启 69 | pool.execute(task); 70 | return; 71 | } 72 | 73 | //延时操作 74 | dispatcher.schedule(new Runnable() { 75 | @Override 76 | public void run() { 77 | //在将来的某个时间执行给定的命令。该命令可以在新线程、池线程或调用线程中执行 78 | pool.execute(task); 79 | } 80 | }, delay, TimeUnit.MILLISECONDS); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/utils/ThreadToolUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.yc.ycthreadpoollib.utils; 18 | 19 | 20 | import com.yc.ycthreadpoollib.callback.ThreadCallback; 21 | 22 | /** 23 | *
24 |  *     @author yangchong
25 |  *     blog  : https://www.jianshu.com/p/53017c3fc75d
26 |  *     time  : 2017/08/22
27 |  *     desc  : 工具
28 |  *     revise:
29 |  * 
30 | */ 31 | public final class ThreadToolUtils { 32 | 33 | /** 34 | * 标志:在android平台上 35 | */ 36 | private static boolean isAndroid; 37 | 38 | /* 39 | * 静态代码块 40 | * 判断是否是android环境 41 | * Class.forName(xxx.xx.xx) 返回的是一个类对象 42 | * 首先要明白在java里面任何class都要装载在虚拟机上才能运行。 43 | */ 44 | static { 45 | try { 46 | Class.forName("android.os.Build"); 47 | isAndroid = true; 48 | } catch (Exception e) { 49 | isAndroid = false; 50 | } 51 | } 52 | 53 | /** 54 | * 判断是否是Android 55 | * 56 | * @return true表示Android 57 | */ 58 | public static boolean isIsAndroid() { 59 | return isAndroid; 60 | } 61 | 62 | /** 63 | * 重置线程名并设置UnCaughtExceptionHandler包装回调,以便在发生异常时通知用户 64 | * 65 | * @param thread The thread who should be reset. 66 | * @param name non-null, thread name 67 | * @param callback a callback to notify user. 68 | */ 69 | public static void resetThread(Thread thread, final String name, final ThreadCallback callback) { 70 | //捕获单个线程的异常 71 | thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 72 | @Override 73 | public void uncaughtException(Thread t, Throwable e) { 74 | //如果线程出现了异常,则会回调该方法 75 | if (callback != null) { 76 | callback.onError(name, e); 77 | } 78 | } 79 | }); 80 | thread.setName(name); 81 | } 82 | 83 | public static void sleepThread(long time) { 84 | if (time <= 0) { 85 | return; 86 | } 87 | try { 88 | Thread.sleep(time); 89 | } catch (InterruptedException e) { 90 | throw new RuntimeException("Thread has been interrupted", e); 91 | } 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/wrapper/CallableWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.yc.ycthreadpoollib.wrapper; 18 | 19 | 20 | import com.yc.ycthreadpoollib.config.ThreadConfigs; 21 | 22 | import java.util.concurrent.Callable; 23 | 24 | import com.yc.ycthreadpoollib.callback.NormalCallback; 25 | import com.yc.ycthreadpoollib.callback.ThreadCallback; 26 | import com.yc.ycthreadpoollib.utils.ThreadToolUtils; 27 | 28 | /** 29 | *
30 |  *     @author yangchong
31 |  *     blog  : https://github.com/yangchong211
32 |  *     time  : 2017/08/22
33 |  *     desc  : CallableWrapper
34 |  *     revise:
35 |  * 
36 | */ 37 | public final class CallableWrapper implements Callable { 38 | 39 | private String name; 40 | private ThreadCallback callback; 41 | private Callable proxy; 42 | 43 | /** 44 | * 构造方法 45 | * @param configs thread配置,主要参数有:线程name,延迟time,回调callback,异步callback 46 | * @param proxy 线程优先级 47 | */ 48 | public CallableWrapper(ThreadConfigs configs, Callable proxy) { 49 | this.name = configs.name; 50 | this.proxy = proxy; 51 | this.callback = new NormalCallback(configs.callback, configs.deliver, configs.asyncCallback); 52 | } 53 | 54 | /** 55 | * 详细可以看我的GitHub:https://github.com/yangchong211 56 | * 自定义Callable继承Callable类,Callable 是在 JDK1.5 增加的。 57 | * Callable 的 call() 方法可以返回值和抛出异常 58 | * @return 泛型 59 | * @throws Exception 异常 60 | */ 61 | @Override 62 | public T call() { 63 | ThreadToolUtils.resetThread(Thread.currentThread(),name,callback); 64 | if (callback != null) { 65 | //开始 66 | callback.onStart(name); 67 | } 68 | T t = null; 69 | try { 70 | t = proxy == null ? null : proxy.call(); 71 | } catch (Exception e) { 72 | e.printStackTrace(); 73 | //异常错误 74 | if(callback!=null){ 75 | callback.onError(name,e); 76 | } 77 | }finally { 78 | //完成 79 | if (callback != null) { 80 | callback.onCompleted(name); 81 | } 82 | } 83 | return t; 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | 54 | 55 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/wrapper/RunnableWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.yc.ycthreadpoollib.wrapper; 18 | 19 | 20 | import com.yc.ycthreadpoollib.config.ThreadConfigs; 21 | 22 | import java.util.concurrent.Callable; 23 | 24 | import com.yc.ycthreadpoollib.callback.NormalCallback; 25 | import com.yc.ycthreadpoollib.utils.ThreadToolUtils; 26 | 27 | /** 28 | *
 29 |  *     @author yangchong
 30 |  *     blog  : https://github.com/yangchong211
 31 |  *     time  : 2017/08/22
 32 |  *     desc  : RunnableWrapper
 33 |  *     revise:
 34 |  * 
35 | */ 36 | public final class RunnableWrapper implements Runnable { 37 | 38 | private String name; 39 | private NormalCallback normal; 40 | private Runnable runnable; 41 | private Callable callable; 42 | 43 | public RunnableWrapper(ThreadConfigs configs) { 44 | this.name = configs.name; 45 | this.normal = new NormalCallback(configs.callback, configs.deliver, configs.asyncCallback); 46 | } 47 | 48 | /** 49 | * 启动异步任务,普通的 50 | * @param runnable runnable 51 | * @return 对象 52 | */ 53 | public RunnableWrapper setRunnable(Runnable runnable) { 54 | this.runnable = runnable; 55 | return this; 56 | } 57 | 58 | /** 59 | * 异步任务,回调用于接收可调用任务的结果 60 | * @param callable callable 61 | * @return 对象 62 | */ 63 | public RunnableWrapper setCallable(Callable callable) { 64 | this.callable = callable; 65 | return this; 66 | } 67 | 68 | /** 69 | * 自定义xxRunnable继承Runnable,实现run方法 70 | * 详细可以看我的GitHub:https://github.com/yangchong211 71 | */ 72 | @Override 73 | public void run() { 74 | //获取线程 75 | Thread current = Thread.currentThread(); 76 | //重置线程 77 | ThreadToolUtils.resetThread(current, name, normal); 78 | //开始 79 | normal.onStart(name); 80 | //注意需要判断runnable,callable非空 81 | // avoid NullPointException 82 | if (runnable != null) { 83 | runnable.run(); 84 | } else if (callable != null) { 85 | try { 86 | Object result = callable.call(); 87 | //监听成功 88 | normal.onSuccess(result); 89 | } catch (Exception e) { 90 | //监听异常 91 | normal.onError(name, e); 92 | } 93 | } 94 | //监听完成 95 | normal.onCompleted(name); 96 | } 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/builder/CustomBuilderImpl.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpoollib.builder; 2 | 3 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 4 | import com.yc.ycthreadpoollib.factory.MyThreadFactory; 5 | import com.yc.ycthreadpoollib.config.ThreadPoolType; 6 | 7 | import java.util.concurrent.BlockingQueue; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.SynchronousQueue; 10 | import java.util.concurrent.ThreadPoolExecutor; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | *
 15 |  *     @author yangchong
 16 |  *     blog  : https://github.com/yangchong211
 17 |  *     time  : 2019/05/17
 18 |  *     desc  : 自定义线程池
 19 |  *     revise: 这个是高级的用法,需要对线程池有较深入的理解才可以使用。否则建议直接使用系统提供api创建线程池
 20 |  * 
21 | */ 22 | public class CustomBuilderImpl extends AbsThreadPoolBuilder { 23 | 24 | private int mCorePoolSize = 1; 25 | private int mMaximumPoolSize = Integer.MAX_VALUE; 26 | private long mKeepAliveTime = 60L; 27 | private TimeUnit mUnit; 28 | private BlockingQueue mWorkQueue; 29 | 30 | public CustomBuilderImpl() { 31 | this.mUnit = TimeUnit.SECONDS; 32 | //设置默认的线程处理的队列 33 | //作用是,把提交的runnable存储到队列里面 34 | this.mWorkQueue = new SynchronousQueue<>(); 35 | } 36 | 37 | public ExecutorService create(MyThreadFactory myThreadFactory) { 38 | ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 39 | this.mCorePoolSize, this.mMaximumPoolSize, 40 | this.mKeepAliveTime, this.mUnit, 41 | this.mWorkQueue, myThreadFactory); 42 | return threadPoolExecutor; 43 | } 44 | 45 | public ThreadPoolType getType() { 46 | return ThreadPoolType.CUSTOM; 47 | } 48 | 49 | /** 50 | * 线程池中核心线程的数量 51 | * 52 | * @param corePoolSize 53 | * @return 54 | */ 55 | public CustomBuilderImpl corePoolSize(int corePoolSize) { 56 | this.mCorePoolSize = corePoolSize; 57 | return this; 58 | } 59 | 60 | /** 61 | * 线程池中的最大线程数,当任务数量超过最大线程数时其它任务可能就会被阻塞 62 | * 63 | * @param maximumPoolSize 64 | * @return 65 | */ 66 | public CustomBuilderImpl maximumPoolSize(int maximumPoolSize) { 67 | this.mMaximumPoolSize = maximumPoolSize; 68 | return this; 69 | } 70 | 71 | /** 72 | * 非核心线程的超时时长,当执行时间超过这个时间时,非核心线程就会被回收 73 | * 74 | * @param keepAliveTime 75 | * @return 76 | */ 77 | public CustomBuilderImpl keepAliveTime(long keepAliveTime) { 78 | this.mKeepAliveTime = keepAliveTime; 79 | return this; 80 | } 81 | 82 | /** 83 | * 枚举时间单位 84 | * 85 | * @param unit 86 | * @return 87 | */ 88 | public CustomBuilderImpl unit(TimeUnit unit) { 89 | this.mUnit = unit; 90 | return this; 91 | } 92 | 93 | /** 94 | * 线程池中的任务队列,我们提交给线程池的runnable会被存储在这个对象上 95 | * 96 | * @param workQueue 97 | * @return 98 | */ 99 | public CustomBuilderImpl workQueue(BlockingQueue workQueue) { 100 | this.mWorkQueue = workQueue; 101 | return this; 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | -------------------------------------------------------------------------------- /ThreadTaskLib/src/main/java/com/yc/taskscheduler/AbsTaskRunnable.java: -------------------------------------------------------------------------------- 1 | package com.yc.taskscheduler; 2 | 3 | import android.util.Log; 4 | 5 | import com.yc.easyexecutor.DelegateTaskExecutor; 6 | 7 | import java.util.concurrent.atomic.AtomicBoolean; 8 | import java.util.concurrent.atomic.AtomicReference; 9 | 10 | 11 | public abstract class AbsTaskRunnable implements Runnable { 12 | 13 | private static final String TAG = "Task"; 14 | private final AtomicBoolean mCanceledAtomic = new AtomicBoolean(false); 15 | private final AtomicReference mTaskThread = new AtomicReference<>(); 16 | 17 | 18 | /** 19 | * 异步线程处理任务,在非主线程执行 20 | * @return 处理后的结果 21 | * @throws InterruptedException 获取InterruptedException异常,来判断任务是否被取消 22 | */ 23 | public abstract R doInBackground() throws InterruptedException; 24 | 25 | /** 26 | * 异步线程处理后返回的结果,在主线程执行 27 | * @param result 结果 28 | */ 29 | public abstract void onSuccess(R result); 30 | 31 | /** 32 | * 异步线程处理出现异常的回调,按需处理,未置成抽象,主线程执行 33 | * @param throwable 异常 34 | */ 35 | public void onFail(Throwable throwable){ 36 | 37 | } 38 | 39 | /** 40 | * 任务被取消的回调,主线程执行 41 | * 42 | */ 43 | public void onCancel(){ 44 | 45 | } 46 | 47 | /** 48 | * 将任务标记为取消,没法真正取消正在执行的任务,只是结果不在onSuccess里回调 49 | * cancel 不一定能让任务停止,和AsyncTask同样道理,可参考 50 | * {#link http://silencedut.com/2016/07/08/%E5%9F%BA%E4%BA%8E%E6%9C%80%E6%96%B0%E7%89%88%E6%9C%AC%E7%9A%84AsyncTask%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB%E5%8F%8AAsyncTask%E7%9A%84%E9%BB%91%E6%9A%97%E9%9D%A2/} 51 | **/ 52 | 53 | void cancel() { 54 | 55 | this.mCanceledAtomic.set(true); 56 | 57 | Thread t = mTaskThread.get(); 58 | if(t!=null) { 59 | Log.d(TAG,"Task cancel: "+t.getName()); 60 | t.interrupt(); 61 | } 62 | DelegateTaskExecutor.getInstance().postToMainThread(new Runnable() { 63 | @Override 64 | public void run() { 65 | onCancel(); 66 | } 67 | }); 68 | } 69 | 70 | /** 71 | * 任务是已取消 72 | * @return 任务是否已被取消 73 | */ 74 | public boolean isCanceled() { 75 | 76 | return mCanceledAtomic.get(); 77 | } 78 | 79 | @Override 80 | public void run() { 81 | try { 82 | 83 | Log.d(TAG,"Task : "+Thread.currentThread().getName()); 84 | mTaskThread.compareAndSet(null,Thread.currentThread()); 85 | 86 | mCanceledAtomic.set(false); 87 | 88 | final R result = doInBackground(); 89 | DelegateTaskExecutor.getInstance().postToMainThread(new Runnable() { 90 | @Override 91 | public void run() { 92 | if(!isCanceled()){ 93 | onSuccess(result); 94 | } 95 | } 96 | }); 97 | } catch (final Throwable throwable) { 98 | 99 | Log.e(TAG,"handle background Task error " +throwable); 100 | DelegateTaskExecutor.getInstance().postToMainThread(new Runnable() { 101 | @Override 102 | public void run() { 103 | if(!isCanceled()){ 104 | onFail(throwable); 105 | } 106 | } 107 | }); 108 | } 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /ThreadPoolLib/src/main/java/com/yc/ycthreadpoollib/callback/NormalCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 yangchong211(github.com/yangchong211) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.yc.ycthreadpoollib.callback; 17 | 18 | 19 | import java.util.concurrent.Executor; 20 | 21 | /** 22 | *
 23 |  *     @author yangchong
 24 |  *     blog  : https://github.com/yangchong211
 25 |  *     time  : 2017/08/22
 26 |  *     desc  : 回调委托类,监听
 27 |  *     revise:
 28 |  * 
29 | */ 30 | public final class NormalCallback implements ThreadCallback, AsyncCallback { 31 | 32 | 33 | private ThreadCallback callback; 34 | private AsyncCallback async; 35 | private Executor deliver; 36 | 37 | public NormalCallback(ThreadCallback callback, Executor deliver, AsyncCallback async) { 38 | this.callback = callback; 39 | this.deliver = deliver; 40 | this.async = async; 41 | } 42 | 43 | /** 44 | * 回调成功 45 | * @param o 46 | */ 47 | @Override 48 | public void onSuccess(final Object o) { 49 | if (async == null) { 50 | return; 51 | } 52 | deliver.execute(new Runnable() { 53 | @Override 54 | public void run() { 55 | try { 56 | //noinspection unchecked 57 | async.onSuccess(o); 58 | } catch (Throwable t) { 59 | onFailed(t); 60 | } 61 | } 62 | }); 63 | } 64 | 65 | /** 66 | * 回调失败 67 | * @param t 异常 68 | */ 69 | @Override 70 | public void onFailed(final Throwable t) { 71 | if (async == null) { 72 | return; 73 | } 74 | deliver.execute(new Runnable() { 75 | @Override 76 | public void run() { 77 | async.onFailed(t); 78 | } 79 | }); 80 | } 81 | 82 | /** 83 | * 回调异常 84 | * @param name 线程name 85 | * @param t 异常 86 | */ 87 | @Override 88 | public void onError(final String name, final Throwable t) { 89 | onFailed(t); 90 | if (callback == null) { 91 | return; 92 | } 93 | deliver.execute(new Runnable() { 94 | @Override 95 | public void run() { 96 | callback.onError(name, t); 97 | } 98 | }); 99 | } 100 | 101 | /** 102 | * 回调完成 103 | * @param name 线程name 104 | */ 105 | @Override 106 | public void onCompleted(final String name) { 107 | if (callback == null) { 108 | return; 109 | } 110 | deliver.execute(new Runnable() { 111 | @Override 112 | public void run() { 113 | callback.onCompleted(name); 114 | } 115 | }); 116 | } 117 | 118 | /** 119 | * 回调开始 120 | * @param name 线程name 121 | */ 122 | @Override 123 | public void onStart(final String name) { 124 | if (callback == null) { 125 | return; 126 | } 127 | deliver.execute(new Runnable() { 128 | @Override 129 | public void run() { 130 | callback.onStart(name); 131 | } 132 | }); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/java/com/yc/ycthreadpool/EasyExecutorActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpool; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | import com.yc.apploglib.AppLogHelper; 12 | import com.yc.easyexecutor.DelegateTaskExecutor; 13 | import com.yc.toastutils.ToastUtils; 14 | 15 | 16 | public class EasyExecutorActivity extends AppCompatActivity implements View.OnClickListener { 17 | 18 | /** 19 | * 开启页面 20 | * 21 | * @param context 上下文 22 | */ 23 | public static void startActivity(Context context) { 24 | try { 25 | Intent target = new Intent(); 26 | target.setClass(context, EasyExecutorActivity.class); 27 | context.startActivity(target); 28 | } catch (Exception e){ 29 | e.printStackTrace(); 30 | } 31 | } 32 | 33 | @Override 34 | protected void onCreate(@Nullable Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_easy_executor); 37 | findViewById(R.id.tv_1).setOnClickListener(this); 38 | findViewById(R.id.tv_2).setOnClickListener(this); 39 | findViewById(R.id.tv_3).setOnClickListener(this); 40 | findViewById(R.id.tv_4).setOnClickListener(this); 41 | findViewById(R.id.tv_5).setOnClickListener(this); 42 | } 43 | 44 | @Override 45 | public void onClick(View v) { 46 | int id = v.getId(); 47 | if (id == R.id.tv_1){ 48 | test1(); 49 | } else if (id == R.id.tv_2){ 50 | test2(); 51 | } else if (id == R.id.tv_3){ 52 | test3(); 53 | } else if (id == R.id.tv_4){ 54 | test4(); 55 | } else if (id == R.id.tv_5){ 56 | test5(); 57 | } 58 | } 59 | 60 | private void test1() { 61 | DelegateTaskExecutor.getInstance().executeOnCore(new Runnable() { 62 | @Override 63 | public void run() { 64 | AppLogHelper.d("EasyExecutor: " , "核心任务的线程池,执行任务"); 65 | } 66 | }); 67 | } 68 | 69 | private void test2() { 70 | DelegateTaskExecutor.getInstance().executeOnDiskIO(new Runnable() { 71 | @Override 72 | public void run() { 73 | AppLogHelper.d("EasyExecutor: " , "IO 密集型任务的线程池,执行任务"); 74 | } 75 | }); 76 | } 77 | 78 | private void test3() { 79 | DelegateTaskExecutor.getInstance().executeOnCpu(new Runnable() { 80 | @Override 81 | public void run() { 82 | AppLogHelper.d("EasyExecutor: " , "CPU 密集型任务的线程池,执行任务"); 83 | } 84 | }); 85 | } 86 | 87 | private void test4() { 88 | DelegateTaskExecutor.getInstance().postToMainThread(new Runnable() { 89 | @Override 90 | public void run() { 91 | AppLogHelper.d("EasyExecutor: " , "UI主线程共有handler对象,执行任务1"); 92 | ToastUtils.showRoundRectToast("UI主线程共有handler对象"); 93 | } 94 | }); 95 | DelegateTaskExecutor.getInstance().executeOnMainThread(new Runnable() { 96 | @Override 97 | public void run() { 98 | AppLogHelper.d("EasyExecutor: " , "UI主线程共有handler对象,执行任务2"); 99 | ToastUtils.showRoundRectToast("UI主线程共有handler对象"); 100 | } 101 | }); 102 | } 103 | 104 | private void test5() { 105 | DelegateTaskExecutor.getInstance().postIoHandler(new Runnable() { 106 | @Override 107 | public void run() { 108 | AppLogHelper.d("EasyExecutor: " , "配合HandlerThread使用的handler,执行任务"); 109 | } 110 | }); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/java/com/yc/easyexecutor/DefaultTaskExecutor.java: -------------------------------------------------------------------------------- 1 | package com.yc.easyexecutor; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | import androidx.annotation.RestrictTo; 9 | 10 | import java.util.concurrent.BlockingQueue; 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.LinkedBlockingQueue; 14 | import java.util.concurrent.RejectedExecutionHandler; 15 | import java.util.concurrent.ThreadPoolExecutor; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | *
 20 |  *     @author yangchong
 21 |  *     email  : yangchong211@163.com
 22 |  *     time   : 2019/5/11
 23 |  *     desc   : task具体实现类
 24 |  *     revise :
 25 |  *     GitHub : https://github.com/yangchong211/YCThreadPool
 26 |  * 
27 | */ 28 | public class DefaultTaskExecutor extends AbsTaskExecutor { 29 | 30 | private final Object mLock = new Object(); 31 | /** 32 | * UI主线程共有handler对象 33 | */ 34 | @Nullable 35 | private volatile Handler mMainHandler; 36 | /** 37 | * 配合handlerThread使用的handler,一般用来执行大量任务 38 | */ 39 | @Nullable 40 | private volatile Handler mIoHandler; 41 | /** 42 | * 核心任务的线程池 43 | */ 44 | private final ExecutorService mCoreExecutor; 45 | /** 46 | * IO 密集型任务的线程池 47 | */ 48 | private final ExecutorService mDiskIO; 49 | /** 50 | * CPU 密集型任务的线程池 51 | */ 52 | private final ThreadPoolExecutor mCPUThreadPoolExecutor; 53 | /** 54 | * CPU 核数 55 | */ 56 | private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 57 | /** 58 | * 线程池线程数 59 | */ 60 | private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 5)); 61 | /** 62 | * 线程池线程数的最大值 63 | */ 64 | private static final int MAXIMUM_POOL_SIZE = CORE_POOL_SIZE; 65 | /** 66 | * 线程空置回收时间 67 | */ 68 | private static final int KEEP_ALIVE_SECONDS = 5; 69 | /** 70 | * 线程池队列 71 | */ 72 | private final BlockingQueue mPoolWorkQueue = new LinkedBlockingQueue<>(); 73 | /** 74 | * 这个是为了保障任务超出BlockingQueue的最大值,且线程池中的线程数已经达到MAXIMUM_POOL_SIZE时候 75 | * 还有任务到来会采取任务拒绝策略,这里定义的策略就是再开一个缓存线程池去执行。 76 | * 当然BlockingQueue默认的最大值是int_max,所以理论上这里是用不到的 77 | */ 78 | private final RejectedExecutionHandler mHandler = new RejectedExecutionHandler() { 79 | @Override 80 | public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 81 | Executors.newCachedThreadPool().execute(r); 82 | } 83 | }; 84 | 85 | public DefaultTaskExecutor() { 86 | //主要是处理io密集流 87 | mDiskIO = Executors.newFixedThreadPool(4, new MyThreadFactory("LoggerTask #")); 88 | //处理比较核心的任务 89 | mCoreExecutor = Executors.newSingleThreadExecutor(new MyThreadFactory("ScheduleTask") { 90 | @Override 91 | public Thread newThread(Runnable r) { 92 | //创建线程 93 | Thread scheduleTask = new Thread(r); 94 | scheduleTask.setName("ScheduleTask"); 95 | return scheduleTask; 96 | } 97 | }); 98 | //处理cpu密集任务 99 | mCPUThreadPoolExecutor = new ThreadPoolExecutor( 100 | CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, 101 | mPoolWorkQueue, Executors.defaultThreadFactory(), mHandler); 102 | mCPUThreadPoolExecutor.allowCoreThreadTimeOut(true); 103 | } 104 | 105 | 106 | @Override 107 | public void executeOnCore(@NonNull Runnable runnable) { 108 | if (runnable != null && mDiskIO != null) { 109 | mDiskIO.execute(runnable); 110 | } 111 | } 112 | 113 | @Override 114 | public void executeOnDiskIO(@NonNull Runnable runnable) { 115 | if (runnable != null && mCoreExecutor != null) { 116 | mCoreExecutor.execute(runnable); 117 | } 118 | } 119 | 120 | @Override 121 | public void executeOnCpu(@NonNull Runnable runnable) { 122 | if (runnable != null && mCPUThreadPoolExecutor != null) { 123 | mCPUThreadPoolExecutor.execute(runnable); 124 | } 125 | } 126 | 127 | @Override 128 | public void postToMainThread(@NonNull Runnable runnable) { 129 | mMainHandler = getMainHandler(); 130 | if (mMainHandler != null && runnable != null) { 131 | mMainHandler.post(runnable); 132 | } 133 | } 134 | 135 | @Override 136 | public Handler getMainHandler() { 137 | if (mMainHandler == null) { 138 | synchronized (mLock) { 139 | if (mMainHandler == null) { 140 | mMainHandler = new SafeHandler(Looper.getMainLooper()); 141 | } 142 | } 143 | } 144 | return mMainHandler; 145 | } 146 | 147 | /** 148 | * 使用 149 | * @param runnable 150 | */ 151 | @Override 152 | public void postIoHandler(@NonNull Runnable runnable) { 153 | if (mIoHandler == null){ 154 | synchronized (mLock) { 155 | if (mIoHandler == null) { 156 | mIoHandler = TaskHandlerThread.get("postIoHandler") 157 | .getHandler("postIoHandler"); 158 | } 159 | } 160 | } 161 | if (mIoHandler != null && runnable != null) { 162 | mIoHandler.post(runnable); 163 | } 164 | } 165 | 166 | /** 167 | * 判断是否是主线程 168 | * @return true表示主线程 169 | */ 170 | @Override 171 | public boolean isMainThread() { 172 | return Looper.getMainLooper().getThread() == Thread.currentThread(); 173 | } 174 | 175 | 176 | } 177 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /read/05.线程池复用原理.md: -------------------------------------------------------------------------------- 1 | #### 目录介绍 2 | - 01.先思考一个问题 3 | - 02.execute和submit 4 | - 03.ThreadPoolExecutor分析 5 | - 04.回到分析问题 6 | 7 | 8 | 9 | ### 01.先思考一个问题 10 | - 为了保证项目中线程数量不会乱飙升,不好管理,我们会使用线程池,保证线程在我们的管理之下。 11 | - 我们也经常说:使用线程池复用线程。那么问题是:线程池中的线程是如何复用的?是执行完成后销毁,再新建几个放那;还是始终是那几个线程(针对 coreSize 线程)。 12 | 13 | 14 | 15 | ### 02.execute和submit 16 | - 调用线程池的execute方法(ExecutorService的submit方法最终也是调用execute)传进去的Runnable,并不会直接以new Thread(runnable).start()的方式来执行,而是通过一个正在运行的线程来调用我们传进去的Runnable的run方法的。 17 | - 那么,这个正在运行的线程,在执行完传进去的Runnable的run方法后会销毁吗?看情况。 18 | - 大部分场景下,我们都是通过Executors的newXXX方法来创建线程池的,就拿newCachedThreadPool来说: 19 | ```java 20 | public static ExecutorService newCachedThreadPool() { 21 | return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); 22 | } 23 | ``` 24 | - 看第三个参数(keepAliveTime):60L,后面的单位是秒,也就是说,newCachedThreadPool方法返回的线程池,它的工作线程(也就是用来调用Runnable的run方法的线程)的空闲等待时长为60秒,如果超过了60秒没有获取到新的任务,那么这个工作线程就会结束。如果在60秒内接到了新的任务,那么它会在新任务结束后重新等待。 25 | - 还有另一种常用的线程池,通过newFixedThreadPool方法创建的: 26 | ```java 27 | public static ExecutorService newFixedThreadPool(int nThreads) { 28 | return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); 29 | } 30 | ``` 31 | - 它跟上面的newCachedThreadPool方法一样,创建的都是ThreadPoolExecutor的对象,只是参数不同而已。 32 | 可以看到第三个参数设置成了0,这就说明,如果当前工作线程数 > corePoolSize时,并且有工作线程在执行完上一个任务后没拿到新的任务,那么这个工作线程就会立即结束。 33 | 再看第二个参数(maximumPoolSize),它设置成了跟corePoolSize一样大,也就是说当前工作线程数 永远不会大于 corePoolSize了,这样的话,即使有工作线程是空闲的,也不会主动结束,会一直等待下一个任务的到来。 34 | 35 | 36 | ### 03.ThreadPoolExecutor分析 37 | - 来探究一下ThreadPoolExecutor是如何管理线程的,先来看精简后的execute方法: 38 | - 逻辑很清晰:当execute方法被调用时,如果当前工作线程 < corePoolSize(上面ThreadPoolExecutor构造方法的第一个参数)的话,就会创建新的线程,否则加入队列。加入队列后如果没有工作线程在运行,也会创建一个。 39 | ```java 40 | private final BlockingQueue workQueue; 41 | 42 | public void execute(Runnable command) { 43 | int c = ctl.get(); 44 | //当前工作线程还没满 45 | if (workerCountOf(c) < corePoolSize) { 46 | //可以创建新的工作线程来执行这个任务 47 | if (addWorker(command, true)){ 48 | //添加成功直接返回 49 | return; 50 | } 51 | } 52 | 53 | //如果工作线程满了的话,会加入到阻塞队列中 54 | if (workQueue.offer(command)) { 55 | int recheck = ctl.get(); 56 | //加入到队列之后,如果当前没有工作线程,那么就会创建一个工作线程 57 | if (workerCountOf(recheck) == 0) 58 | addWorker(null, false); 59 | } 60 | } 61 | ``` 62 | - 接着看它是怎么创建新线程的: 63 | - 主要操作是再次检查,然后创建Worker对象,并且把worker对象店家到HashSet集合中,最后启动工作线程。 64 | ```java 65 | private final HashSet workers = new HashSet<>(); 66 | 67 | private boolean addWorker(Runnable firstTask, boolean core) { 68 | //再次检查 69 | int wc = workerCountOf(c); 70 | if (wc >= CAPACITY || wc >= corePoolSize) 71 | return false; 72 | 73 | boolean workerStarted = false; 74 | Worker w = null; 75 | //创建Worker对象 76 | w = new Worker(firstTask); 77 | //添加到集合中 78 | workers.add(w); 79 | final Thread t = w.thread; 80 | //启动工作线程 81 | t.start(); 82 | workerStarted = true; 83 | 84 | return workerStarted; 85 | } 86 | ``` 87 | - 看看Worker里面是怎么样的: 88 | - 可以看到,这个Worker也是一个Runnable。构造方法里面还创建了一个Thread,这个Thread对象,对应了上面addWorker方法启动的那个thread。 89 | ```java 90 | private final class Worker extends AbstractQueuedSynchronizer implements Runnable { 91 | final Thread thread; 92 | Runnable firstTask; 93 | 94 | Worker(Runnable firstTask) { 95 | this.firstTask = firstTask; 96 | this.thread = getThreadFactory().newThread(this); 97 | } 98 | 99 | public void run() { 100 | runWorker(this); 101 | } 102 | } 103 | ``` 104 | - 再看Worker类中的run方法,它调用了runWorker,并把自己传了进去: 105 | - **Worker里面的firstTask,就是我们通过execute方法传进去的Runnable,可以看到它会在这个方法里面被执行**。 106 | - **执行完成之后,接着就会通过getTask方法尝试从等待队列中(上面的workQueue)获取下一个任务,如果getTask方法返回null的话,那么这个工作线程就会结束**。 107 | ```java 108 | final void runWorker(Worker w) { 109 | Runnable task = w.firstTask; 110 | w.firstTask = null; 111 | 112 | while (task != null || (task = getTask()) != null) { 113 | try { 114 | task.run(); 115 | } finally { 116 | task = null; 117 | w.completedTasks++; 118 | } 119 | } 120 | } 121 | ``` 122 | - 最后看看runWorker方法中的getTask方法 123 | ``` 124 | private Runnable getTask() { 125 | boolean timedOut = false; // Did the last poll() time out? 126 | 127 | for (; ; ) { 128 | int c = ctl.get(); 129 | int wc = workerCountOf(c); 130 | 131 | //如果当前工作线程数大于指定的corePoolSize的话,就要视情况结束工作线程 132 | boolean timed = wc > corePoolSize; 133 | 134 | //(当前工作线程数 > 指定的最大线程数 || (工作线程数 > 指定的核心线程数 && 上一次被标记超时了)) && (当前工作线程数有2个以上 || 等待队列现在是空的) 135 | if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { 136 | return null; 137 | } 138 | //如果当前工作线程数大于指定的corePoolSize,就看能不能在keepAliveTime时间内获取到新任务 139 | //如果线程数没有 > corePoolSize的话,就会一直等待 140 | Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); 141 | if (r != null) 142 | return r; 143 | //没能在keepAliveTime时间内获取到新任务,标记已超时 144 | timedOut = true; 145 | } 146 | } 147 | ``` 148 | 149 | 150 | ### 04.回到分析问题 151 | #### 4.1 newCachedThreadPool 152 | - 如果是newCachedThreadPool的话: 153 | - 核心线程数 0; 154 | - 最大线程数 Integer.MAX_VALUE; 155 | - 空闲线程存活时间 60秒; 156 | - 那么当执行到getTask方法时,里面的timed肯定每次都是true的,也就是每次获取任务的时候,最多只能等60秒,如果60秒内没有获取到新的任务,那么getTask就会返回null(工作线程会结束)。 157 | 158 | 159 | #### 4.2 newFixedThreadPool 160 | - 像newFixedThreadPool,如果我们传的是5,Executors.newFixedThreadPool(5)。那么,它的参数是: 161 | - 核心线程数 5; 162 | - 最大线程数 5; 163 | - 空闲线程存活时间 0秒; 164 | - 在判断当前工作线程数是否大于核心线程数的时候,肯定就是false了,因为在前面提交任务的时候,就已经有判断:小于核心线程数才创建新的工作线程。 165 | - timed是false的话,从workQueue中取任务的时候,调用的就不是poll方法,而是take方法,这个take方法会一直阻塞,直到拿到元素为止。 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/java/com/yc/ycthreadpool/ExecutorsTestActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpool; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Bundle; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | import android.util.Log; 9 | import android.view.View; 10 | 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | 17 | 18 | public class ExecutorsTestActivity extends AppCompatActivity implements View.OnClickListener { 19 | 20 | int number = 200; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_test); 26 | findViewById(R.id.tv_0).setOnClickListener(this); 27 | findViewById(R.id.tv_1).setOnClickListener(this); 28 | findViewById(R.id.tv_2).setOnClickListener(this); 29 | findViewById(R.id.tv_3).setOnClickListener(this); 30 | findViewById(R.id.tv_4).setOnClickListener(this); 31 | } 32 | 33 | @Override 34 | public void onClick(View v) { 35 | switch (v.getId()){ 36 | case R.id.tv_0: 37 | //最普通方式 38 | createThread(); 39 | break; 40 | case R.id.tv_1: 41 | newFixedThreadPool(); 42 | //newFixedThreadPool1(); 43 | break; 44 | case R.id.tv_2: 45 | newSingleThreadExecutor(); 46 | break; 47 | case R.id.tv_3: 48 | newCachedThreadPool(); 49 | break; 50 | case R.id.tv_4: 51 | newScheduledThreadPool(); 52 | break; 53 | default: 54 | break; 55 | } 56 | } 57 | 58 | /** 59 | * 最普通创建线程的方式 60 | */ 61 | private void createThread() { 62 | for (int i = 1; i <= number; i++) { 63 | final int index = i; 64 | //线程抢占资源,会占用cpu 65 | new Thread(new Runnable() { 66 | @Override 67 | public void run() { 68 | String threadName = Thread.currentThread().getName(); 69 | Log.e("潇湘剑雨Thread", "线程:"+threadName+",正在执行第" + index + "个任务"); 70 | try { 71 | Thread.sleep(500); 72 | } catch (InterruptedException e) { 73 | e.printStackTrace(); 74 | } 75 | } 76 | }).start(); 77 | } 78 | //todo 模拟线程导致OOM操作 79 | } 80 | 81 | /** 82 | * 由于newFixedThreadPool只有核心线程,并且这些线程都不会被回收,也就是它能够更快速的响应外界请求 83 | */ 84 | private void newFixedThreadPool() { 85 | ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); 86 | for (int i = 1; i <= number; i++) { 87 | final int index = i; 88 | fixedThreadPool.execute(new Runnable() { 89 | @Override 90 | public void run() { 91 | String threadName = Thread.currentThread().getName(); 92 | Log.e("潇湘剑雨newFixedThreadPool", "线程:"+threadName+",正在执行第" + index + "个任务"); 93 | try { 94 | Thread.sleep(500); 95 | } catch (InterruptedException e) { 96 | e.printStackTrace(); 97 | } 98 | } 99 | }); 100 | } 101 | } 102 | 103 | private void newSingleThreadExecutor() { 104 | ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); 105 | for (int i = 1; i <= number; i++) { 106 | final int index = i; 107 | singleThreadPool.execute(new Runnable() { 108 | @SuppressLint("LongLogTag") 109 | @Override 110 | public void run() { 111 | String threadName = Thread.currentThread().getName(); 112 | Log.e("潇湘剑雨 newSingleThreadExecutor", "线程:"+threadName+",正在执行第" + index + "个任务"); 113 | try { 114 | Thread.sleep(500); 115 | } catch (InterruptedException e) { 116 | e.printStackTrace(); 117 | } 118 | } 119 | }); 120 | } 121 | } 122 | 123 | /** 124 | * 缓存线程池, 125 | */ 126 | private void newCachedThreadPool() { 127 | ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); 128 | for (int i = 1; i <= number; i++) { 129 | final int index = i; 130 | cachedThreadPool.execute(new Runnable() { 131 | @Override 132 | public void run() { 133 | String threadName = Thread.currentThread().getName(); 134 | Log.e("潇湘剑雨newCachedThreadPool", "线程:" + threadName + ",正在执行第" + index + "个任务"); 135 | try { 136 | long time = 500; 137 | Thread.sleep(time); 138 | } catch (InterruptedException e) { 139 | e.printStackTrace(); 140 | } 141 | } 142 | }); 143 | } 144 | } 145 | 146 | private void newScheduledThreadPool() { 147 | ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); 148 | //延迟2秒后执行该任务 149 | scheduledThreadPool.schedule(new Runnable() { 150 | @SuppressLint("LongLogTag") 151 | @Override 152 | public void run() { 153 | String threadName = Thread.currentThread().getName(); 154 | Log.e("潇湘剑雨newScheduledThreadPool", "线程:" + threadName + ",正在执行"); 155 | } 156 | }, 2, TimeUnit.SECONDS); 157 | //延迟1秒后,每隔2秒执行一次该任务 158 | scheduledThreadPool.scheduleAtFixedRate(new Runnable() { 159 | @Override 160 | public void run() { 161 | String threadName = Thread.currentThread().getName(); 162 | Log.e("潇湘剑雨", "线程:" + threadName + ",正在执行"); 163 | } 164 | }, 1, 2, TimeUnit.SECONDS); 165 | } 166 | 167 | 168 | } 169 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /ThreadTaskLib/src/main/java/com/yc/taskscheduler/TaskScheduler.java: -------------------------------------------------------------------------------- 1 | package com.yc.taskscheduler; 2 | 3 | 4 | import android.os.Handler; 5 | import android.os.Process; 6 | import android.util.Log; 7 | 8 | import com.yc.easyexecutor.DelegateTaskExecutor; 9 | import com.yc.easyexecutor.InterExecutorLog; 10 | import com.yc.easyexecutor.SafeHandler; 11 | import com.yc.easyexecutor.TaskHandlerThread; 12 | 13 | import java.util.concurrent.BlockingQueue; 14 | import java.util.concurrent.ExecutionException; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Future; 17 | import java.util.concurrent.LinkedBlockingQueue; 18 | import java.util.concurrent.ScheduledExecutorService; 19 | import java.util.concurrent.ScheduledThreadPoolExecutor; 20 | import java.util.concurrent.SynchronousQueue; 21 | import java.util.concurrent.ThreadPoolExecutor; 22 | import java.util.concurrent.TimeUnit; 23 | import java.util.concurrent.TimeoutException; 24 | 25 | 26 | public final class TaskScheduler { 27 | 28 | private volatile static TaskScheduler sTaskScheduler; 29 | private static final String TAG = "TaskScheduler"; 30 | private final ExecutorService mParallelExecutor ; 31 | private final ExecutorService mTimeOutExecutor ; 32 | private InterExecutorLog mILog = new InterExecutorLog() { 33 | @Override 34 | public void info(String info) { 35 | Log.i(TAG,info); 36 | } 37 | 38 | @Override 39 | public void error(String error) { 40 | Log.e(TAG,error); 41 | } 42 | }; 43 | 44 | private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 45 | private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); 46 | private static final int MAXIMUM_POOL_SIZE = CORE_POOL_SIZE * 2 + 1; 47 | private static final long KEEP_ALIVE = 60L; 48 | private static final BlockingQueue POOL_WORK_QUEUE = 49 | new LinkedBlockingQueue<>(128); 50 | 51 | public static TaskScheduler getInstance() { 52 | if(sTaskScheduler==null) { 53 | synchronized (TaskScheduler.class) { 54 | if(sTaskScheduler==null) { 55 | sTaskScheduler = new TaskScheduler(); 56 | } 57 | } 58 | } 59 | return sTaskScheduler; 60 | } 61 | 62 | private TaskScheduler() { 63 | mParallelExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE, 64 | KEEP_ALIVE,TimeUnit.SECONDS,POOL_WORK_QUEUE, MyThreadFactory.TASK_SCHEDULER_FACTORY); 65 | /* 66 | 没有核心线程的线程池要用 SynchronousQueue 而不是LinkedBlockingQueue,SynchronousQueue是一个只有一个任务的队列, 67 | 这样每次就会创建非核心线程执行任务,因为线程池任务放入队列的优先级比创建非核心线程优先级大. 68 | */ 69 | mTimeOutExecutor = new ThreadPoolExecutor(0,MAXIMUM_POOL_SIZE, 70 | KEEP_ALIVE,TimeUnit.SECONDS,new SynchronousQueue(), MyThreadFactory.TIME_OUT_THREAD_FACTORY); 71 | } 72 | 73 | public static void addLogImpl(InterExecutorLog taskLog) { 74 | if(taskLog != null) { 75 | getInstance().mILog = taskLog; 76 | } 77 | } 78 | 79 | public static ExecutorService executorService() { 80 | return getInstance().mParallelExecutor; 81 | } 82 | 83 | /** 84 | * 获取回调到handlerName线程的handler.一般用于在一个后台线程执行同一种任务,避免线程安全问题。如数据库,文件操作 85 | * @param handlerName 线程名 86 | * @return 异步任务handler 87 | */ 88 | public static Handler provideHandler(String handlerName) { 89 | TaskHandlerThread taskHandlerThread = TaskHandlerThread.get(handlerName); 90 | taskHandlerThread.setPriority(Process.THREAD_PRIORITY_BACKGROUND); 91 | taskHandlerThread.start(); 92 | return new SafeHandler(taskHandlerThread.getLooper()); 93 | } 94 | 95 | /** 96 | * 主线程周期性执行任务,默认立刻执行,之后间隔period执行,不需要时注意取消,每次执行时如果有相同的任务,默认会先取消 97 | * @param task 执行的任务 98 | */ 99 | public static void scheduleTask(final SchedulerTask task) { 100 | task.canceled.compareAndSet(true,false); 101 | final ScheduledExecutorService service = new ScheduledThreadPoolExecutor( 102 | 1, MyThreadFactory.SCHEDULER_THREAD_FACTORY); 103 | service.scheduleAtFixedRate(new Runnable() { 104 | @Override 105 | public void run() { 106 | if(task.canceled.get()) { 107 | service.shutdownNow(); 108 | }else { 109 | if(task.mainThread) { 110 | DelegateTaskExecutor.getInstance().postToMainThread(task); 111 | }else { 112 | task.run(); 113 | } 114 | } 115 | } 116 | }, task.startDelayMillisecond, task.periodMillisecond, TimeUnit.MILLISECONDS); 117 | } 118 | 119 | /** 120 | * 取消周期性任务 121 | * @param schedulerTask 任务对象 122 | */ 123 | public static void stopScheduleTask(final SchedulerTask schedulerTask) { 124 | schedulerTask.canceled.compareAndSet(false,true); 125 | } 126 | 127 | 128 | /** 129 | *执行一个后台任务,无回调 130 | * **/ 131 | public static void execute(Runnable task) { 132 | getInstance().mILog.info("execute Runnable"+task.toString()); 133 | getInstance().mParallelExecutor.execute(task); 134 | } 135 | 136 | /** 137 | *执行一个后台任务,如果不需回调 138 | * @see #execute(Runnable) 139 | **/ 140 | public static void execute(AbsTaskRunnable task) { 141 | getInstance().mILog.info("execute task"+task.toString()); 142 | getInstance().mParallelExecutor.execute(task); 143 | } 144 | 145 | /** 146 | * 取消一个任务 147 | * @param task 被取消的任务 148 | */ 149 | public static void cancelTask(AbsTaskRunnable task) { 150 | if(task!=null) { 151 | task.cancel(); 152 | } 153 | } 154 | 155 | /** 156 | * 使用一个单独的线程池来执行超时任务,避免引起他线程不够用导致超时 157 | * @param timeOutMillis 超时时间,单位毫秒 158 | ** 通过实现error(Exception) 判断是否为 TimeoutException 来判断是否超时, 159 | * 不能100%保证实际的超时时间就是timeOutMillis,但一般没必要那么精确 160 | * */ 161 | public static void executeTimeOutTask(final long timeOutMillis, final AbsTaskRunnable timeOutTask) { 162 | Future future = getInstance().mTimeOutExecutor.submit(timeOutTask); 163 | getInstance().mTimeOutExecutor.execute(new Runnable() { 164 | @Override 165 | public void run() { 166 | try { 167 | future.get(timeOutMillis,TimeUnit.MILLISECONDS); 168 | } catch (InterruptedException | ExecutionException | TimeoutException e ) { 169 | DelegateTaskExecutor.getInstance().postToMainThread(new Runnable() { 170 | @Override 171 | public void run() { 172 | if (!timeOutTask.isCanceled()) { 173 | timeOutTask.cancel(); 174 | } 175 | } 176 | }); 177 | } 178 | } 179 | }); 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /EasyExecutor/src/main/java/com/yc/easyexecutor/DelegateTaskExecutor.java: -------------------------------------------------------------------------------- 1 | package com.yc.easyexecutor; 2 | 3 | import android.os.Handler; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | import androidx.lifecycle.Lifecycle; 8 | import androidx.lifecycle.LifecycleOwner; 9 | 10 | import java.util.concurrent.Executor; 11 | 12 | /** 13 | *
 14 |  *     @author yangchong
 15 |  *     email  : yangchong211@163.com
 16 |  *     time   : 2019/5/11
 17 |  *     desc   : task代理类,简单xia。具体用这个
 18 |  *     revise :
 19 |  *     GitHub : https://github.com/yangchong211/YCThreadPool
 20 |  * 
21 | */ 22 | public class DelegateTaskExecutor extends AbsTaskExecutor { 23 | 24 | private static volatile DelegateTaskExecutor sInstance; 25 | 26 | @NonNull 27 | private AbsTaskExecutor mDelegate; 28 | 29 | @NonNull 30 | private final AbsTaskExecutor mDefaultTaskExecutor; 31 | 32 | @NonNull 33 | private static final Executor sMainThreadExecutor = new Executor() { 34 | @Override 35 | public void execute(Runnable command) { 36 | if (command != null) { 37 | getInstance().postToMainThread(command); 38 | } 39 | } 40 | }; 41 | 42 | @NonNull 43 | private static final Executor sCoreThreadExecutor = new Executor() { 44 | @Override 45 | public void execute(Runnable command) { 46 | if (command != null) { 47 | getInstance().executeOnCore(command); 48 | } 49 | } 50 | }; 51 | 52 | @NonNull 53 | private static final Executor sIOThreadExecutor = new Executor() { 54 | @Override 55 | public void execute(Runnable command) { 56 | if (command != null) { 57 | getInstance().executeOnDiskIO(command); 58 | } 59 | } 60 | }; 61 | 62 | @NonNull 63 | private static final Executor sCpuThreadExecutor = new Executor() { 64 | @Override 65 | public void execute(Runnable command) { 66 | if (command != null) { 67 | getInstance().executeOnCpu(command); 68 | } 69 | } 70 | }; 71 | 72 | private DelegateTaskExecutor() { 73 | mDefaultTaskExecutor = new DefaultTaskExecutor(); 74 | mDelegate = mDefaultTaskExecutor; 75 | } 76 | 77 | @NonNull 78 | public static DelegateTaskExecutor getInstance() { 79 | if (sInstance != null) { 80 | return sInstance; 81 | } 82 | synchronized (DelegateTaskExecutor.class) { 83 | if (sInstance == null) { 84 | sInstance = new DelegateTaskExecutor(); 85 | } 86 | } 87 | return sInstance; 88 | } 89 | 90 | public void setDelegate(@Nullable AbsTaskExecutor taskExecutor) { 91 | mDelegate = taskExecutor == null ? mDefaultTaskExecutor : taskExecutor; 92 | } 93 | 94 | @Override 95 | public void executeOnCore(@NonNull Runnable runnable) { 96 | if (runnable != null) { 97 | mDelegate.executeOnCore(runnable); 98 | } 99 | } 100 | 101 | @Override 102 | public void executeOnDiskIO(@Nullable Runnable runnable) { 103 | if (runnable != null) { 104 | mDelegate.executeOnDiskIO(runnable); 105 | } 106 | } 107 | 108 | @Override 109 | public void executeOnCpu(@NonNull Runnable runnable) { 110 | if (runnable != null) { 111 | mDelegate.executeOnCpu(runnable); 112 | } 113 | } 114 | 115 | @Override 116 | public void postToMainThread(@Nullable Runnable runnable) { 117 | if (runnable != null) { 118 | mDelegate.postToMainThread(runnable); 119 | } 120 | } 121 | 122 | @Override 123 | public Handler getMainHandler() { 124 | return mDelegate.getMainHandler(); 125 | } 126 | 127 | @Override 128 | public void postIoHandler(@NonNull Runnable runnable) { 129 | if (runnable != null) { 130 | mDelegate.postIoHandler(runnable); 131 | } 132 | } 133 | 134 | @Override 135 | public void executeOnMainThread(@NonNull Runnable runnable) { 136 | super.executeOnMainThread(runnable); 137 | } 138 | 139 | /** 140 | * 使用HandlerThread和handler处理消息 141 | * 142 | * @return MainThreadExecutor 143 | */ 144 | @NonNull 145 | public Executor getMainThreadExecutor() { 146 | return sMainThreadExecutor; 147 | } 148 | 149 | /** 150 | * 获取核心线程池,主要是执行一些核心任务,比如初始化相关操作 151 | * 152 | * @return CoreThreadExecutor 153 | */ 154 | @NonNull 155 | public Executor getCoreThreadExecutor() { 156 | return sCoreThreadExecutor; 157 | } 158 | 159 | /** 160 | * 获得io密集型线程池,有好多任务其实占用的CPU time非常少,所以使用缓存线程池,基本上来着不拒 161 | * 162 | * @return IOThreadPoolExecutor 163 | */ 164 | @NonNull 165 | public Executor getIOThreadExecutor() { 166 | return sIOThreadExecutor; 167 | } 168 | 169 | /** 170 | * 获得cpu密集型线程池,因为占据CPU的时间片过多的话会影响性能,所以这里控制了最大并发,防止主线程的时间片减少 171 | * 172 | * @return CPUThreadPoolExecutor 173 | */ 174 | @NonNull 175 | public Executor getCpuThreadExecutor() { 176 | return sCpuThreadExecutor; 177 | } 178 | 179 | /** 180 | * 判断是否是主线程 181 | * 182 | * @return true表示主线成 183 | */ 184 | @Override 185 | public boolean isMainThread() { 186 | return mDelegate.isMainThread(); 187 | } 188 | 189 | 190 | /** 191 | * 执行有生命周期的任务 192 | */ 193 | public Runnable postToMainThread(LifecycleOwner lifecycleOwner, Runnable runnable) { 194 | LifecycleRunnable lifecycleRunnableDelegate = new LifecycleRunnable(lifecycleOwner, 195 | getMainHandler(), Lifecycle.Event.ON_DESTROY,runnable); 196 | mDelegate.postToMainThread(lifecycleRunnableDelegate); 197 | return lifecycleRunnableDelegate; 198 | } 199 | 200 | 201 | /** 202 | * 执行有生命周期的任务,指定Lifecycle.Event 203 | */ 204 | public Runnable postToMainThread(LifecycleOwner lifecycleOwner, 205 | Lifecycle.Event targetEvent, 206 | Runnable runnable) { 207 | LifecycleRunnable lifecycleRunnableDelegate = new LifecycleRunnable(lifecycleOwner, 208 | getMainHandler(),targetEvent,runnable); 209 | mDelegate.postToMainThread(lifecycleRunnableDelegate); 210 | return lifecycleRunnableDelegate; 211 | } 212 | 213 | 214 | public void postToMainThread(Runnable runnable,long delayed) { 215 | getMainHandler().postDelayed(runnable,delayed); 216 | } 217 | 218 | public Runnable postToMainThread(LifecycleOwner lifecycleOwner,Runnable runnable,long delayed) { 219 | LifecycleRunnable lifecycleRunnableDelegate = new LifecycleRunnable(lifecycleOwner, 220 | getMainHandler(),Lifecycle.Event.ON_DESTROY,runnable); 221 | getMainHandler().postDelayed(lifecycleRunnableDelegate,delayed); 222 | return lifecycleRunnableDelegate; 223 | } 224 | 225 | public void removeUICallback(Runnable runnable) { 226 | getMainHandler().removeCallbacks(runnable); 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /read/03.线程池深入思考.md: -------------------------------------------------------------------------------- 1 | #### 目录介绍 2 | - 01.先看问题思考 3 | - 02.典型回答 4 | - 03.考点分析说明 5 | - 04.知识拓展 6 | - 05.线程池状态 7 | - 06.线程池实践 8 | - 07.线程池大小选择策略 9 | 10 | 11 | 12 | ### 01.先看问题思考 13 | - 线程是不能够重复启动的,创建或销毁线程存在一定的开销,所以利用线程池技术来提高系统资源利用效率,并简化线程管理,已经是非常成熟的选择。 14 | - 今天我要问你的问题是,Java 并发类库提供的线程池有哪几种? 分别有什么特点? 15 | 16 | 17 | ### 02.典型回答 18 | - 通常开发者都是利用 Executors 提供的通用线程池创建方法,去创建不同配置的线程池,主要区别在于不同的 ExecutorService 类型或者不同的初始参数。Executors 目前提供了 5 种不同的线程池创建配置: 19 | - newCachedThreadPool(),它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列。 20 | - newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads。 21 | - newSingleThreadExecutor(),它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目。 22 | - newSingleThreadScheduledExecutor() 和 newScheduledThreadPool(int corePoolSize),创建的是个 ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。 23 | - newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。 24 | 25 | 26 | ### 03.考点分析说明 27 | - Java 并发包中的 Executor 框架无疑是并发编程中的重点,今天的题目考察的是对几种标准线程池的了解,我提供的是一个针对最常见的应用方式的回答。 28 | - 在大多数应用场景下,使用 Executors 提供的 5 个静态工厂方法就足够了,但是仍然可能需要直接利用 ThreadPoolExecutor 等构造函数创建,这就要求你对线程构造方式有进一步的了解,你需要明白线程池的设计和结构。 29 | - 另外,线程池这个定义就是个容易让人误解的术语,因为 ExecutorService 除了通常意义上“池”的功能,还提供了更全面的线程管理、任务提交等方法。 30 | - Executor 框架可不仅仅是线程池,我觉得至少下面几点值得深入学习: 31 | - 掌握 Executor 框架的主要内容,至少要了解组成与职责,掌握基本开发用例中的使用。 32 | - 对线程池和相关并发工具类型的理解,甚至是源码层面的掌握。 33 | - 实践中有哪些常见问题,基本的诊断思路是怎样的。 34 | - 如何根据自身应用特点合理使用线程池。 35 | 36 | 37 | ### 04.知识拓展 38 | - 首先,我们来看看 Executor 框架的基本组成,请参考下面的类图。 39 | - ![image](https://img-blog.csdn.net/20180820214644749) 40 | - 我们从整体上把握一下各个类型的主要设计目的: 41 | - Executor 是一个基础的接口,其初衷是将任务提交和任务执行细节解耦,这一点可以体会其定义的唯一方法。 42 | ``` 43 | void execute(Runnable command); 44 | ``` 45 | - Executor 的设计是源于 Java 早期线程 API 使用的教训,开发者在实现应用逻辑时,被太多线程创建、调度等不相关细节所打扰。就像我们进行 HTTP 通信,如果还需要自己操作 TCP 握手,开发效率低下,质量也难以保证。 46 | - ExecutorService 则更加完善,不仅提供 service 的管理功能,比如 shutdown 等方法,也提供了更加全面的提交任务机制,如返回Future而不是 void 的 submit 方法。 47 | ``` 48 | Future submit(Callable task); 49 | ``` 50 | - 注意,这个例子输入的可是Callable,它解决了 Runnable 无法返回结果的困扰。 51 | - Java 标准类库提供了几种基础实现,比如ThreadPoolExecutor、ScheduledThreadPoolExecutor、ForkJoinPool。这些线程池的设计特点在于其高度的可调节性和灵活性,以尽量满足复杂多变的实际应用场景,我会进一步分析其构建部分的源码,剖析这种灵活性的源头。 52 | - Executors 则从简化使用的角度,为我们提供了各种方便的静态工厂方法。 53 | - 下面我就从源码角度,分析线程池的设计与实现,我将主要围绕最基础的 ThreadPoolExecutor 源码。ScheduledThreadPoolExecutor 是 ThreadPoolExecutor 的扩展,主要是增加了调度逻辑,如想深入了解,你可以参考相关教程。而 ForkJoinPool 则是为 ForkJoinTask 定制的线程池,与通常意义的线程池有所不同。 54 | - 这部分内容比较晦涩,罗列概念也不利于你去理解,所以我会配合一些示意图来说明。在现实应用中,理解应用与线程池的交互和线程池的内部工作过程,你可以参考下图。 55 | - ![image](https://img-blog.csdn.net/20180820215057504) 56 | - 简单理解一下: 57 | - 工作队列负责存储用户提交的各个任务,这个工作队列,可以是容量为 0 的 SynchronousQueue(使用 newCachedThreadPool),也可以是像固定大小线程池(newFixedThreadPool)那样使用 LinkedBlockingQueue。 58 | ``` 59 | private final BlockingQueue workQueue; 60 | ``` 61 | - 内部的“线程池”,这是指保持工作线程的集合,线程池需要在运行过程中管理线程创建、销毁。例如,对于带缓存的线程池,当任务压力较大时,线程池会创建新的工作线程;当业务压力退去,线程池会在闲置一段时间(默认 60 秒)后结束线程。 62 | ``` 63 | private final HashSet workers = new HashSet<>(); 64 | ``` 65 | - 线程池的工作线程被抽象为静态内部类 Worker,基于AQS实现。 66 | - ThreadFactory 提供上面所需要的创建线程逻辑。 67 | - 如果任务提交时被拒绝,比如线程池已经处于 SHUTDOWN 状态,需要为其提供处理逻辑,Java 标准库提供了类似ThreadPoolExecutor.AbortPolicy等默认实现,也可以按照实际需求自定义。 68 | - 从上面的分析,就可以看出线程池的几个基本组成部分,一起都体现在线程池的构造函数中,从字面我们就可以大概猜测到其用意: 69 | - corePoolSize,所谓的核心线程数,可以大致理解为长期驻留的线程数目(除非设置了 allowCoreThreadTimeOut)。对于不同的线程池,这个值可能会有很大区别,比如 newFixedThreadPool 会将其设置为 nThreads,而对于 newCachedThreadPool 则是为 0。 70 | - maximumPoolSize,顾名思义,就是线程不够时能够创建的最大线程数。同样进行对比,对于 newFixedThreadPool,当然就是 nThreads,因为其要求是固定大小,而 newCachedThreadPool 则是 Integer.MAX_VALUE。 71 | - keepAliveTime 和 TimeUnit,这两个参数指定了额外的线程能够闲置多久,显然有些线程池不需要它。 72 | - workQueue,工作队列,必须是 BlockingQueue。 73 | 74 | 75 | 76 | ### 05.线程池状态 77 | - 进一步分析,线程池既然有生命周期,它的状态是如何表征的呢? 78 | - 这里有一个非常有意思的设计,ctl变量被赋予了双重角色,通过高低位的不同,既表示线程池状态,又表示工作线程数目,这是一个典型的高效优化。试想,实际系统中,虽然我们可以指定线程极限为Integer.MAX_VALUE,但是因为资源限制,这只是个理论值,所以完全可以将空闲位赋予其他意义。 79 | ```java 80 | private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 81 | // 真正决定了工作线程数的理论上限 82 | private static final int COUNT_BITS = Integer.SIZE - 3; 83 | private static final int COUNT_MASK = (1 << COUNT_BITS) - 1; 84 | // 线程池状态,存储在数字的高位 85 | private static final int RUNNING = -1 << COUNT_BITS; 86 | … 87 | // Packing and unpacking ctl 88 | private static int runStateOf(int c) { return c & ~COUNT_MASK; } 89 | private static int workerCountOf(int c) { return c & COUNT_MASK; } 90 | private static int ctlOf(int rs, int wc) { return rs | wc; } 91 | ``` 92 | - 为了让你能对线程生命周期有个更加清晰的印象,我这里画了一个简单的状态流转图,对线程池的可能状态和其内部方法之间进行了对应,如果有不理解的方法,请参考 Javadoc。注意,实际 Java 代码中并不存在所谓 Idle 状态,我添加它仅仅是便于理解。 93 | - ![image](https://img-blog.csdn.net/20180820220845460) 94 | - 前面都是对线程池属性和构建等方面的分析,下面我选择典型的 execute 方法,来看看其是如何工作的,具体逻辑请参考我添加的注释,配合代码更加容易理解。 95 | ```java 96 | public void execute(Runnable command) { 97 | … 98 | int c = ctl.get(); 99 | // 检查工作线程数目,低于corePoolSize则添加Worker 100 | if (workerCountOf(c) < corePoolSize) { 101 | if (addWorker(command, true)) 102 | return; 103 | c = ctl.get(); 104 | } 105 | // isRunning就是检查线程池是否被shutdown 106 | // 工作队列可能是有界的,offer是比较友好的入队方式 107 | if (isRunning(c) && workQueue.offer(command)) { 108 | int recheck = ctl.get(); 109 | // 再次进行防御性检查 110 | if (! isRunning(recheck) && remove(command)) 111 | reject(command); 112 | else if (workerCountOf(recheck) == 0) 113 | addWorker(null, false); 114 | } 115 | // 尝试添加一个worker,如果失败意味着已经饱和或者被shutdown了 116 | else if (!addWorker(command, false)) 117 | reject(command); 118 | } 119 | ``` 120 | 121 | 122 | ### 06.线程池实践 123 | - 线程池虽然为提供了非常强大、方便的功能,但是也不是银弹,使用不当同样会导致问题。我这里介绍些典型情况,经过前面的分析,很多方面可以自然的推导出来。 124 | - 避免任务堆积。前面我说过 newFixedThreadPool 是创建指定数目的线程,但是其工作队列是无界的,如果工作线程数目太少,导致处理跟不上入队的速度,这就很有可能占用大量系统内存,甚至是出现 OOM。诊断时,你可以使用 jmap 之类的工具,查看是否有大量的任务对象入队。 125 | - 避免过度扩展线程。我们通常在处理大量短时任务时,使用缓存的线程池,比如在最新的 HTTP/2 client API 中,目前的默认实现就是如此。我们在创建线程池的时候,并不能准确预计任务压力有多大、数据特征是什么样子(大部分请求是 1K 、100K 还是 1M 以上?),所以很难明确设定一个线程数目。 126 | - 另外,如果线程数目不断增长(可以使用 jstack 等工具检查),也需要警惕另外一种可能性,就是线程泄漏,这种情况往往是因为任务逻辑有问题,导致工作线程迟迟不能被释放。建议你排查下线程栈,很有可能多个线程都是卡在近似的代码处。 127 | - 避免死锁等同步问题,对于死锁的场景和排查。 128 | - 尽量避免在使用线程池时操作ThreadLocal,通过今天的线程池学习,应该更能理解其原因,工作线程的生命周期通常都会超过任务的生命周期。 129 | 130 | 131 | 132 | ### 07.线程池大小选择策略 133 | - 上面我已经介绍过,线程池大小不合适,太多或太少,都会导致麻烦,所以我们需要去考虑一个合适的线程池大小。虽然不能完全确定,但是有一些相对普适的规则和思路。 134 | - 如果我们的任务主要是进行计算,那么就意味着 CPU 的处理能力是稀缺的资源,我们能够通过大量增加线程数提高计算能力吗?往往是不能的,如果线程太多,反倒可能导致大量的上下文切换开销。所以,这种情况下,通常建议按照 CPU 核的数目 N 或者 N+1。 135 | - 如果是需要较多等待的任务,例如 I/O 操作比较多,可以参考 Brain Goetz 推荐的计算方法: 136 | ``` 137 | 线程数 = CPU核数 × 目标CPU利用率 ×(1 + 平均等待时间/平均工作时间) 138 | ``` 139 | - 这些时间并不能精准预计,需要根据采样或者概要分析等方式进行计算,然后在实际中验证和调整。 140 | - 另外,在实际工作中,不要把解决问题的思路全部指望到调整线程池上,很多时候架构上的改变更能解决问题,比如利用背压机制的Reactive Stream、合理的拆分等。 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/java/com/yc/ycthreadpool/ThreadPollActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpool; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.View; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | import com.yc.apploglib.AppLogHelper; 12 | import com.yc.toastutils.ToastUtils; 13 | import com.yc.ycthreadpoollib.PoolThread; 14 | import com.yc.ycthreadpoollib.callback.AsyncCallback; 15 | import com.yc.ycthreadpoollib.callback.ThreadCallback; 16 | import com.yc.ycthreadpoollib.deliver.AndroidDeliver; 17 | 18 | import java.util.concurrent.Callable; 19 | import java.util.concurrent.ExecutionException; 20 | import java.util.concurrent.Future; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | 24 | public class ThreadPollActivity extends AppCompatActivity implements View.OnClickListener { 25 | 26 | private PoolThread executor; 27 | 28 | /** 29 | * 开启页面 30 | * 31 | * @param context 上下文 32 | */ 33 | public static void startActivity(Context context) { 34 | try { 35 | Intent target = new Intent(); 36 | target.setClass(context, ThreadPollActivity.class); 37 | context.startActivity(target); 38 | } catch (Exception e){ 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | setContentView(R.layout.activity_poll_thread); 48 | findViewById(R.id.tv_1).setOnClickListener(this); 49 | findViewById(R.id.tv_2_1).setOnClickListener(this); 50 | findViewById(R.id.tv_2_2).setOnClickListener(this); 51 | findViewById(R.id.tv_3).setOnClickListener(this); 52 | findViewById(R.id.tv_4).setOnClickListener(this); 53 | findViewById(R.id.tv_4_2).setOnClickListener(this); 54 | 55 | 56 | // 计算可使用的最大内存 57 | final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 58 | // 取四分之一的可用内存作为缓存 59 | final int cacheSize = maxMemory / 4; 60 | } 61 | 62 | @Override 63 | public void onClick(View v) { 64 | switch (v.getId()){ 65 | case R.id.tv_1: 66 | startThread1(); 67 | break; 68 | case R.id.tv_2_1: 69 | startThread2(); 70 | break; 71 | case R.id.tv_2_2: 72 | if (executor!=null){ 73 | executor.stop(); 74 | } else { 75 | ToastUtils.showRoundRectToast("请先开启异步耗时操作"); 76 | } 77 | break; 78 | case R.id.tv_3: 79 | startThread3(); 80 | break; 81 | case R.id.tv_4: 82 | startThread4(); 83 | break; 84 | case R.id.tv_4_2: 85 | startThread5(); 86 | break; 87 | default: 88 | break; 89 | } 90 | } 91 | 92 | private void startThread1() { 93 | PoolThread executor = App.getInstance().getExecutor(); 94 | executor.setName("最简单的线程调用方式"); 95 | executor.setDeliver(new AndroidDeliver()); 96 | executor.execute(new Runnable() { 97 | @Override 98 | public void run() { 99 | AppLogHelper.d("PoolThread: ","最简单的线程调用方式"); 100 | } 101 | }); 102 | } 103 | 104 | 105 | private void startThread2() { 106 | executor = PoolThread.ThreadBuilder.createSingle().build(); 107 | executor.setName("异步回调"); 108 | executor.setDelay(2,TimeUnit.SECONDS); 109 | // 启动异步任务 110 | executor.async(new Callable(){ 111 | @Override 112 | public Boolean call() throws Exception { 113 | AppLogHelper.d("PoolThread: ","耗时操作"); 114 | Thread.sleep(5000); 115 | // 做一些操作 116 | return true; 117 | } 118 | }, new AsyncCallback() { 119 | @Override 120 | public void onSuccess(Boolean isLogin) { 121 | AppLogHelper.d("PoolThread: ","成功" + isLogin); 122 | ToastUtils.showRoundRectToast("异步成功"); 123 | } 124 | 125 | @Override 126 | public void onFailed(Throwable t) { 127 | AppLogHelper.d("PoolThread: ","失败"+t.getMessage()); 128 | ToastUtils.showRoundRectToast("异步失败"); 129 | } 130 | 131 | @Override 132 | public void onStart(String threadName) { 133 | AppLogHelper.d("PoolThread: ","开始"); 134 | ToastUtils.showRoundRectToast("异步开始"); 135 | } 136 | }); 137 | executor.setCallback(new ThreadCallback() { 138 | @Override 139 | public void onError(String threadName, Throwable t) { 140 | AppLogHelper.d("PoolThread: ","异常:" + t.getMessage()); 141 | } 142 | 143 | @Override 144 | public void onCompleted(String threadName) { 145 | AppLogHelper.d("PoolThread: ","成功onCompleted"); 146 | } 147 | 148 | @Override 149 | public void onStart(String threadName) { 150 | AppLogHelper.d("PoolThread: ","开始onStart"); 151 | } 152 | }); 153 | } 154 | 155 | 156 | private void startThread3() { 157 | PoolThread executor = App.getInstance().getExecutor(); 158 | executor.setName("延迟时间执行任务"); 159 | executor.setDelay(2, TimeUnit.SECONDS); 160 | executor.setDeliver(new AndroidDeliver()); 161 | executor.execute(new Runnable() { 162 | @Override 163 | public void run() { 164 | AppLogHelper.d("PoolThread: ","延迟时间执行任务"); 165 | } 166 | }); 167 | } 168 | 169 | 170 | private void startThread4() { 171 | PoolThread executor = App.getInstance().getExecutor(); 172 | //设置为当前的任务设置线程名 173 | executor.setName("延迟时间执行任务"); 174 | //设置当前任务的延迟时间 175 | executor.setDelay(2, TimeUnit.SECONDS); 176 | //设置当前任务的线程传递 177 | executor.setDeliver(new AndroidDeliver()); 178 | executor.setCallback(new ThreadCallback() { 179 | @Override 180 | public void onError(String threadName, Throwable t) { 181 | AppLogHelper.d("PoolThread: ","startThread4---onError"); 182 | } 183 | 184 | @Override 185 | public void onCompleted(String threadName) { 186 | AppLogHelper.d("PoolThread: ","startThread4---onCompleted"); 187 | } 188 | 189 | @Override 190 | public void onStart(String threadName) { 191 | AppLogHelper.d("PoolThread: ","startThread4---onStart"); 192 | } 193 | }); 194 | executor.submit(new Callable() { 195 | @Override 196 | public String call() throws Exception { 197 | AppLogHelper.d("PoolThread: ","startThread4---call"); 198 | Thread.sleep(2000); 199 | String str = "小杨逗比"; 200 | return str; 201 | } 202 | }); 203 | } 204 | 205 | 206 | 207 | private void startThread5() { 208 | PoolThread executor = App.getInstance().getExecutor(); 209 | //设置为当前的任务设置线程名 210 | executor.setName("延迟时间执行任务"); 211 | //设置当前任务的延迟时间 212 | executor.setDelay(2, TimeUnit.SECONDS); 213 | //设置当前任务的线程传递 214 | executor.setDeliver(new AndroidDeliver()); 215 | Future submit = executor.submit(new Callable() { 216 | @Override 217 | public String call() throws Exception { 218 | AppLogHelper.d("PoolThread: ","startThread5---call"); 219 | Thread.sleep(2000); 220 | String str = "小杨逗比"; 221 | return str; 222 | } 223 | }); 224 | try { 225 | String result = submit.get(); 226 | AppLogHelper.d("PoolThread: ","startThread5-----"+result); 227 | ToastUtils.showRoundRectToast(result); 228 | } catch (InterruptedException e) { 229 | e.printStackTrace(); 230 | } catch (ExecutionException e) { 231 | e.printStackTrace(); 232 | } 233 | } 234 | 235 | } 236 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/java/com/yc/ycthreadpool/ThreadPoolExecutorActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpool; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.app.AppCompatActivity; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Vector; 14 | import java.util.concurrent.ArrayBlockingQueue; 15 | import java.util.concurrent.ExecutionException; 16 | import java.util.concurrent.Future; 17 | import java.util.concurrent.ThreadPoolExecutor; 18 | import java.util.concurrent.TimeUnit; 19 | import java.util.concurrent.TimeoutException; 20 | 21 | 22 | public class ThreadPoolExecutorActivity extends AppCompatActivity implements View.OnClickListener { 23 | 24 | 25 | @Override 26 | protected void onCreate(@Nullable Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_test_thread_count); 29 | 30 | findViewById(R.id.tv_0).setOnClickListener(this); 31 | findViewById(R.id.tv_1).setOnClickListener(this); 32 | findViewById(R.id.tv_2).setOnClickListener(this); 33 | findViewById(R.id.tv_3).setOnClickListener(this); 34 | findViewById(R.id.tv_4).setOnClickListener(this); 35 | findViewById(R.id.tv_5).setOnClickListener(this); 36 | findViewById(R.id.tv_6).setOnClickListener(this); 37 | } 38 | 39 | 40 | @Override 41 | public void onClick(View view) { 42 | switch (view.getId()){ 43 | case R.id.tv_0: 44 | try { 45 | test1(1); 46 | } catch (InterruptedException e) { 47 | e.printStackTrace(); 48 | } catch (ExecutionException e) { 49 | e.printStackTrace(); 50 | } catch (TimeoutException e) { 51 | e.printStackTrace(); 52 | } 53 | break; 54 | case R.id.tv_1: 55 | try { 56 | test1(2); 57 | } catch (InterruptedException e) { 58 | e.printStackTrace(); 59 | } catch (ExecutionException e) { 60 | e.printStackTrace(); 61 | } catch (TimeoutException e) { 62 | e.printStackTrace(); 63 | } 64 | break; 65 | case R.id.tv_2: 66 | break; 67 | case R.id.tv_3: 68 | break; 69 | case R.id.tv_4: 70 | break; 71 | case R.id.tv_5: 72 | break; 73 | case R.id.tv_6: 74 | break; 75 | default: 76 | break; 77 | } 78 | } 79 | 80 | // 初始化线程池 81 | // corePoolSize 线程池中的核心线程数 82 | // maximumPoolSize 线程最大容量数,达到最大值后新创建的线程会被阻塞 83 | // keepAliveTime 非核心线程闲置时的超时时长,对于非核心线程,闲置时间超过这个时间,非核心线程就会被回收。 84 | // unit 时间单位 85 | // workQueue 工作队列 86 | // handler 处理任务的 87 | private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 88 | 8, 8, 10, 89 | TimeUnit.SECONDS, new ArrayBlockingQueue(1000), 90 | new ThreadPoolExecutor.DiscardOldestPolicy()); 91 | 92 | public void test1(int type) throws InterruptedException, ExecutionException, TimeoutException{ 93 | 94 | int cores = Runtime.getRuntime().availableProcessors(); 95 | 96 | int requestNum = 100; 97 | System.out.println("CPU核数 " + cores); 98 | 99 | List> futureList = new ArrayList>(); 100 | Vector wholeTimeList = new Vector(); 101 | Vector runTimeList = new Vector(); 102 | 103 | for (int i = 0; i < requestNum; i++) { 104 | if (type==1){ 105 | Future future = threadPool.submit(new CPUTypeTest(runTimeList, wholeTimeList)); 106 | futureList.add(future); 107 | } else { 108 | Future future = threadPool.submit(new IOTypeTest(runTimeList, wholeTimeList)); 109 | futureList.add(future); 110 | } 111 | } 112 | 113 | for (Future future : futureList) { 114 | //获取线程执行结果 115 | future.get(requestNum, TimeUnit.SECONDS); 116 | } 117 | 118 | long wholeTime = 0; 119 | for (int i = 0; i < wholeTimeList.size(); i++) { 120 | wholeTime = wholeTimeList.get(i) + wholeTime; 121 | } 122 | 123 | long runTime = 0; 124 | for (int i = 0; i < runTimeList.size(); i++) { 125 | runTime = runTimeList.get(i) + runTime; 126 | } 127 | 128 | System.out.println("平均每个线程整体花费时间: " +wholeTime/wholeTimeList.size()); 129 | System.out.println("平均每个线程执行花费时间: " +runTime/runTimeList.size()); 130 | } 131 | 132 | public class CPUTypeTest implements Runnable { 133 | 134 | //整体执行时间,包括在队列中等待的时间 135 | List wholeTimeList; 136 | //真正执行时间 137 | List runTimeList; 138 | 139 | private long initStartTime = 0; 140 | 141 | /** 142 | * 构造函数 143 | * @param runTimeList 144 | * @param wholeTimeList 145 | */ 146 | public CPUTypeTest(List runTimeList, List wholeTimeList) { 147 | initStartTime = System.currentTimeMillis(); 148 | this.runTimeList = runTimeList; 149 | this.wholeTimeList = wholeTimeList; 150 | } 151 | 152 | /** 153 | * 判断素数 154 | * @param number 155 | * @return 156 | */ 157 | public boolean isPrime(final int number) { 158 | if (number <= 1) { 159 | return false; 160 | } 161 | 162 | for (int i = 2; i <= Math.sqrt(number); i++) { 163 | if (number % i == 0) { 164 | return false; 165 | } 166 | } 167 | return true; 168 | } 169 | 170 | public int countPrimes(final int lower, final int upper) { 171 | int total = 0; 172 | for (int i = lower; i <= upper; i++) { 173 | if (isPrime(i)) { 174 | total++; 175 | } 176 | } 177 | return total; 178 | } 179 | 180 | @Override 181 | public void run() { 182 | long start = System.currentTimeMillis(); 183 | try { 184 | Thread.sleep(1000); 185 | } catch (InterruptedException e) { 186 | // TODO Auto-generated catch block 187 | e.printStackTrace(); 188 | } 189 | countPrimes(1, 1000000); 190 | long end = System.currentTimeMillis(); 191 | 192 | long wholeTime = end - initStartTime; 193 | long runTime = end - start; 194 | wholeTimeList.add(wholeTime); 195 | runTimeList.add(runTime); 196 | System.out.println("单个线程花费时间:" + (end - start)); 197 | } 198 | } 199 | 200 | 201 | public class IOTypeTest implements Runnable { 202 | 203 | 204 | //整体执行时间,包括在队列中等待的时间 205 | Vector wholeTimeList; 206 | //真正执行时间 207 | Vector runTimeList; 208 | 209 | private long initStartTime = 0; 210 | 211 | /** 212 | * 构造函数 213 | * @param runTimeList 214 | * @param wholeTimeList 215 | */ 216 | public IOTypeTest(Vector runTimeList, Vector wholeTimeList) { 217 | initStartTime = System.currentTimeMillis(); 218 | this.runTimeList = runTimeList; 219 | this.wholeTimeList = wholeTimeList; 220 | } 221 | 222 | /** 223 | *IO操作 224 | */ 225 | public void readAndWrite() throws IOException { 226 | InputStream in = ThreadPoolExecutorActivity.this.getAssets().open("evaluation.config"); 227 | int size = in.available(); 228 | byte[] buffer = new byte[size]; 229 | in.read(buffer); 230 | in.close(); 231 | 232 | 233 | // File sourceFile = new File(""); 234 | // //创建输入流 235 | // BufferedReader input = new BufferedReader(new FileReader(sourceFile)); 236 | // //读取源文件,写入到新的文件 237 | // String line = null; 238 | // while((line = input.readLine()) != null){ 239 | // System.out.println(line); 240 | // } 241 | // //关闭输入输出流 242 | // input.close(); 243 | } 244 | 245 | public void run() { 246 | long start = System.currentTimeMillis(); 247 | try { 248 | try { 249 | Thread.sleep(2000); 250 | } catch (InterruptedException e) { 251 | e.printStackTrace(); 252 | } 253 | readAndWrite(); 254 | } catch (IOException e) { 255 | e.printStackTrace(); 256 | } 257 | long end = System.currentTimeMillis(); 258 | 259 | long wholeTime = end - initStartTime; 260 | long runTime = end - start; 261 | wholeTimeList.add(wholeTime); 262 | runTimeList.add(runTime); 263 | System.out.println("单个线程花费时间:" + (end - start)); 264 | } 265 | } 266 | 267 | } 268 | -------------------------------------------------------------------------------- /ThreadBusiness/src/main/java/com/yc/ycthreadpool/ThreadActivity.java: -------------------------------------------------------------------------------- 1 | package com.yc.ycthreadpool; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.Nullable; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import android.view.View; 8 | 9 | import java.util.concurrent.locks.Condition; 10 | import java.util.concurrent.locks.Lock; 11 | import java.util.concurrent.locks.ReentrantLock; 12 | 13 | 14 | 15 | public class ThreadActivity extends AppCompatActivity { 16 | 17 | @Override 18 | protected void onCreate(@Nullable Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_thread); 21 | findViewById(R.id.tv_1).setOnClickListener(new View.OnClickListener() { 22 | @Override 23 | public void onClick(View view) { 24 | test1(); 25 | } 26 | }); 27 | findViewById(R.id.tv_2).setOnClickListener(new View.OnClickListener() { 28 | @Override 29 | public void onClick(View view) { 30 | test2(); 31 | } 32 | }); 33 | findViewById(R.id.tv_3).setOnClickListener(new View.OnClickListener() { 34 | @Override 35 | public void onClick(View view) { 36 | test3(); 37 | } 38 | }); 39 | findViewById(R.id.tv_4).setOnClickListener(new View.OnClickListener() { 40 | @Override 41 | public void onClick(View view) { 42 | test4(); 43 | } 44 | }); 45 | findViewById(R.id.tv_51).setOnClickListener(new View.OnClickListener() { 46 | @Override 47 | public void onClick(View view) { 48 | test51(); 49 | } 50 | }); 51 | findViewById(R.id.tv_52).setOnClickListener(new View.OnClickListener() { 52 | @Override 53 | public void onClick(View view) { 54 | test52(); 55 | } 56 | }); 57 | } 58 | 59 | private void test2() { 60 | // 创建3个线程对象 61 | SellTicktes2 t1 = new SellTicktes2() ; 62 | SellTicktes2 t2 = new SellTicktes2() ; 63 | SellTicktes2 t3 = new SellTicktes2() ; 64 | // 设置名称 65 | t1.setName("窗口1") ; 66 | t2.setName("窗口2") ; 67 | t3.setName("窗口3") ; 68 | // 启动线程 69 | t1.start() ; 70 | t2.start() ; 71 | t3.start() ; 72 | } 73 | 74 | public static class SellTicktes2 extends Thread { 75 | private static int num = 100 ; 76 | @Override 77 | public void run() { 78 | // 模拟售票 79 | while(true) { 80 | if( num > 0 ) { 81 | try { 82 | Thread.sleep(100) ; 83 | } catch (InterruptedException e) { 84 | e.printStackTrace(); 85 | } 86 | System.out.println("test2"+Thread.currentThread().getName() + "正在出售" + (num--) + "张票"); 87 | } 88 | } 89 | } 90 | } 91 | 92 | private void test1() { 93 | /** 94 | * 需求:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。 95 | */ 96 | // 创建3个线程对象 97 | SellTicktes t1 = new SellTicktes() ; 98 | SellTicktes t2 = new SellTicktes() ; 99 | SellTicktes t3 = new SellTicktes() ; 100 | // 设置名称 101 | t1.setName("窗口1") ; 102 | t2.setName("窗口2") ; 103 | t3.setName("窗口3") ; 104 | // 启动线程 105 | t1.start() ; 106 | t2.start() ; 107 | t3.start() ; 108 | } 109 | 110 | public static class SellTicktes extends Thread { 111 | private static int num = 100 ; 112 | @Override 113 | public void run() { 114 | /** 115 | * 定义总票数 116 | * 117 | * 如果我们把票数定义成了局部变量,那么表示的意思是每一个窗口出售了各自的100张票; 而我们的需求是: 总共有100张票 118 | * 而这100张票要被3个窗口出售; 因此我们就不能把票数定义成局部变量,只能定义成成员变量 119 | */ 120 | // 模拟售票 121 | while(true) { 122 | if( num > 0 ) { 123 | System.out.println("test1"+Thread.currentThread().getName() + "正在出售" + (num--) + "张票"); 124 | } 125 | } 126 | } 127 | } 128 | 129 | 130 | /** 131 | * 现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 132 | */ 133 | private void test4(){ 134 | Thread t1 = new Thread(new Runnable() { 135 | @Override 136 | public void run() { 137 | System.out.println("test4"+Thread.currentThread().getName() + "线程执行" + "Thread1"); 138 | } 139 | }); 140 | Thread t2 = new Thread(new Runnable() { 141 | @Override 142 | public void run() { 143 | System.out.println("test4"+Thread.currentThread().getName() + "线程执行" + "Thread2"); 144 | } 145 | }); 146 | Thread t3 = new Thread(new Runnable() { 147 | @Override 148 | public void run() { 149 | System.out.println("test4"+Thread.currentThread().getName() + "线程执行" + "Thread3"); 150 | } 151 | }); 152 | 153 | t1.start(); 154 | t2.start(); 155 | t3.start(); 156 | 157 | try { 158 | t1.join(); 159 | } catch (InterruptedException e) { 160 | e.printStackTrace(); 161 | } 162 | try { 163 | t2.join(); 164 | } catch (InterruptedException e) { 165 | e.printStackTrace(); 166 | } 167 | try { 168 | t3.join(); 169 | } catch (InterruptedException e) { 170 | e.printStackTrace(); 171 | } 172 | } 173 | 174 | 175 | private void test3(){ 176 | final ShareThread sh = new ShareThread(); 177 | new Thread(new Runnable() { 178 | 179 | @Override 180 | public void run() { 181 | try { 182 | sh.Test01(); 183 | } catch (InterruptedException e) { 184 | e.printStackTrace(); 185 | } 186 | } 187 | }, "T1").start(); 188 | new Thread(new Runnable() { 189 | 190 | @Override 191 | public void run() { 192 | try { 193 | sh.Test02(); 194 | } catch (InterruptedException e) { 195 | e.printStackTrace(); 196 | } 197 | } 198 | }, "T2").start(); 199 | new Thread(new Runnable() { 200 | 201 | @Override 202 | public void run() { 203 | try { 204 | sh.Test03(); 205 | } catch (InterruptedException e) { 206 | e.printStackTrace(); 207 | } 208 | } 209 | }, "T3").start(); 210 | } 211 | 212 | 213 | class ShareThread { 214 | 215 | // flag作为标记 216 | private int flag = 1; 217 | private Lock lock = new ReentrantLock(); 218 | private Condition c1 = lock.newCondition(); 219 | private Condition c2 = lock.newCondition(); 220 | private Condition c3 = lock.newCondition(); 221 | 222 | public void Test01() throws InterruptedException { 223 | lock.lock(); 224 | try { 225 | while (flag != 1) { 226 | c1.await(); 227 | } 228 | System.out.println("正在执行的是:" + Thread.currentThread().getName()); 229 | flag = 2; 230 | c2.signal();// 通知一个线程来执行 231 | } finally { 232 | lock.unlock(); 233 | } 234 | } 235 | 236 | public void Test02() throws InterruptedException { 237 | lock.lock(); 238 | try { 239 | while (flag != 2) { 240 | c2.await(); 241 | } 242 | System.out.println("正在执行的是:" + Thread.currentThread().getName()); 243 | flag = 3; 244 | c3.signal();// 通知一个线程来执行 245 | } finally { 246 | lock.unlock(); 247 | } 248 | } 249 | 250 | public void Test03() throws InterruptedException { 251 | lock.lock(); 252 | try { 253 | while (flag != 3) { 254 | c3.await(); 255 | } 256 | System.out.println("正在执行的是:" + Thread.currentThread().getName()); 257 | flag = 1; 258 | c1.signal();// 通知一个线程来执行 259 | } finally { 260 | lock.unlock(); 261 | } 262 | } 263 | } 264 | 265 | 266 | 267 | /**-----------------------------------测试volatile关键字---------------------------------------------*/ 268 | 269 | static int i = 10; 270 | public void test51(){ 271 | // MyThread51 t1 = new MyThread51(); 272 | // MyThread51 t2 = new MyThread51(); 273 | // t1.start() ; 274 | // try { 275 | // t1.join(); 276 | // } catch (InterruptedException e) { 277 | // e.printStackTrace(); 278 | // } 279 | // t2.start() ; 280 | 281 | 282 | for (int i=0 ; i<10 ; i++){ 283 | MyThread51 t1 = new MyThread51(); 284 | t1.start() ; 285 | } 286 | } 287 | 288 | static class MyThread51 extends Thread { 289 | @Override 290 | public void run() { 291 | i++; 292 | System.out.println("yc---------" + this.getName()+"----"+i); 293 | } 294 | } 295 | 296 | 297 | static volatile int j = 10; 298 | 299 | public void test52(){ 300 | for (int i=0 ; i<10 ; i++){ 301 | MyThread52 t1 = new MyThread52(); 302 | t1.start() ; 303 | } 304 | } 305 | 306 | static class MyThread52 extends Thread { 307 | @Override 308 | public void run() { 309 | j++; 310 | System.out.println("yc---------" + this.getName()+"----"+j); 311 | } 312 | } 313 | 314 | } 315 | -------------------------------------------------------------------------------- /read/02.线程池案例测试.md: -------------------------------------------------------------------------------- 1 | ### 目录介绍 2 | - **1.ThreadPoolExecutor类介绍** 3 | - 1.1 构造函数 4 | - 1.2 参数解析 5 | - 1.3 遵循的规则 6 | - 1.4 使用线程池管理线程的优点 7 | - 1.5 优先级 8 | - **2.关于线程池的分类** 9 | - 2.1 FixedThreadPool 10 | - 2.2 CachedThreadPool 11 | - 2.3 ScheduledThreadPool 12 | - 2.4 SingleThreadExecutor 13 | - **3.线程池一般用法** 14 | - 3.1 一般方法介绍 15 | - 3.2 newFixedThreadPool的使用 16 | - 3.3 newSingleThreadExecutor的使用 17 | - 3.4 newCachedThreadPool的使用 18 | - 3.5 newScheduledThreadPool的使用 19 | - 3.6 线程创建规则 20 | - **4.线程池封装** 21 | - 4.1 具体可以参考下篇文章 22 | - 4.2 参考博客 23 | 24 | 25 | 26 | ### 好消息 27 | - 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢! 28 | - **链接地址:https://github.com/yangchong211/YCBlogs** 29 | - 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变! 30 | 31 | 32 | ### 1.ThreadPoolExecutor类介绍 33 | #### 1.1 构造函数 34 | - ExecutorService是最初的线程池接口,ThreadPoolExecutor类是对线程池的具体实现,它通过构造方法来配置线程池的参数 35 | ``` 36 | public ThreadPoolExecutor(int corePoolSize, 37 | int maximumPoolSize, 38 | long keepAliveTime, 39 | TimeUnit unit, 40 | BlockingQueue workQueue) { 41 | this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 42 | Executors.defaultThreadFactory(), defaultHandler); 43 | } 44 | ``` 45 | 46 | 47 | 48 | #### 1.2 参数解析 49 | - corePoolSize,线程池中核心线程的数量,默认情况下,即使核心线程没有任务在执行它也存在的,我们固定一定数量的核心线程且它一直存活这样就避免了一般情况下CPU创建和销毁线程带来的开销。我们如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程就会有超时策略,这个时间由keepAliveTime来设定,即keepAliveTime时间内如果核心线程没有回应则该线程就会被终止。allowCoreThreadTimeOut默认为false,核心线程没有超时时间。 50 | - maximumPoolSize,线程池中的最大线程数,当任务数量超过最大线程数时其它任务可能就会被阻塞。最大线程数=核心线程+非核心线程。非核心线程只有当核心线程不够用且线程池有空余时才会被创建,执行完任务后非核心线程会被销毁。 51 | - keepAliveTime,非核心线程的超时时长,当执行时间超过这个时间时,非核心线程就会被回收。当allowCoreThreadTimeOut设置为true时,此属性也作用在核心线程上。[博客](https://github.com/yangchong211/YCBlogs) 52 | - unit,枚举时间单位,TimeUnit。 53 | - workQueue,线程池中的任务队列,我们提交给线程池的runnable会被存储在这个对象上。 54 | 55 | 56 | 57 | #### 1.3 遵循的规则 58 | - 当线程池中的核心线程数量未达到最大线程数时,启动一个核心线程去执行任务; 59 | - 如果线程池中的核心线程数量达到最大线程数时,那么任务会被插入到任务队列中排队等待执行; 60 | - 如果在上一步骤中任务队列已满但是线程池中线程数量未达到限定线程总数,那么启动一个非核心线程来处理任务; 61 | - 如果上一步骤中线程数量达到了限定线程总量,那么线程池则拒绝执行该任务,且ThreadPoolExecutor会调用RejectedtionHandler的rejectedExecution方法来通知调用者。[博客](https://github.com/yangchong211/YCBlogs) 62 | 63 | 64 | 65 | #### 1.4 使用线程池管理线程的优点 66 | - 1、线程的创建和销毁由线程池维护,一个线程在完成任务后并不会立即销毁,而是由后续的任务复用这个线程,从而减少线程的创建和销毁,节约系统的开销 67 | - 2、线程池旨在线程的复用,这就可以节约我们用以往的方式创建线程和销毁所消耗的时间,减少线程频繁调度的开销,从而节约系统资源,提高系统吞吐量 68 | - 3、在执行大量异步任务时提高了性能 69 | - 4、Java内置的一套ExecutorService线程池相关的api,可以更方便的控制线程的最大并发数、线程的定时任务、单线程的顺序执行等 70 | 71 | 72 | #### 1.5 优先级 73 | - 关键点在于 核心线程数、最大线程数和任务队列数,执行流程如下,记住一点,优先级:核心线程数 > 任务队列数 > 最大线程数。 74 | 75 | 76 | 77 | ### 2.关于线程池的分类 78 | #### 2.1 FixedThreadPool 79 | - 通过Executors的newFixedThreadPool()方法创建,它是个线程数量固定的线程池,该线程池的线程全部为核心线程,它们没有超时机制且排队任务队列无限制,因为全都是核心线程,所以响应较快,且不用担心线程会被回收。 80 | 81 | 82 | #### 2.2 CachedThreadPool 83 | - 通过Executors的newCachedThreadPool()方法来创建,它是一个数量无限多的线程池,它所有的线程都是非核心线程,当有新任务来时如果没有空闲的线程则直接创建新的线程不会去排队而直接执行,并且超时时间都是60s,所以此线程池适合执行大量耗时小的任务。由于设置了超时时间为60s,所以当线程空闲一定时间时就会被系统回收,所以理论上该线程池不会有占用系统资源的无用线程。 84 | 85 | 86 | #### 2.3 ScheduledThreadPool 87 | - 通过Executors的newScheduledThreadPool()方法来创建,ScheduledThreadPool线程池像是上两种的合体,它有数量固定的核心线程,且有数量无限多的非核心线程,但是它的非核心线程超时时间是0s,所以非核心线程一旦空闲立马就会被回收。这类线程池适合用于执行定时任务和固定周期的重复任务。 88 | 89 | 90 | #### 2.4 SingleThreadExecutor 91 | - 通过Executors的newSingleThreadExecutor()方法来创建,它内部只有一个核心线程,它确保所有任务进来都要排队按顺序执行。它的意义在于,统一所有的外界任务到同一线程中,让调用者可以忽略线程同步问题。 92 | 93 | 94 | 95 | ### 3.线程池一般用法 96 | #### 3.1 一般方法介绍 97 | - shutDown(),关闭线程池,需要执行完已提交的任务; 98 | - shutDownNow(),关闭线程池,并尝试结束已提交的任务; 99 | - allowCoreThreadTimeOut(boolen),允许核心线程闲置超时回收; 100 | - execute(),提交任务无返回值; 101 | - submit(),提交任务有返回值; 102 | 103 | 104 | 105 | #### 3.2 newFixedThreadPool的使用 106 | - **3.2.1 创建一个newFixedThreadPool线程池** 107 | ``` 108 | private void newFixedThreadPool() { 109 | ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); 110 | for (int i = 1; i <= 20; i++) { 111 | final int index = i; 112 | fixedThreadPool.execute(new Runnable() { 113 | @Override 114 | public void run() { 115 | String threadName = Thread.currentThread().getName(); 116 | Log.e("潇湘剑雨", "线程:"+threadName+",正在执行第" + index + "个任务"); 117 | try { 118 | Thread.sleep(2000); 119 | } catch (InterruptedException e) { 120 | e.printStackTrace(); 121 | } 122 | } 123 | }); 124 | } 125 | } 126 | ``` 127 | 128 | 129 | - **3.2.2 打印日志如下** 130 | - 创建了一个线程数为5的固定线程数量的线程池,同理该线程池支持的线程最大并发数也是5,模拟20个任务让它处理,执行任务。最后我们获取线程的信息,打印日志。 131 | - 日志如图所示: 132 | ![image](https://upload-images.jianshu.io/upload_images/4432347-ba0b93632ac0f80f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 133 | 134 | 135 | 136 | #### 3.3 newSingleThreadExecutor的使用 137 | - **3.3.1 创建一个newSingleThreadExecutor线程池** 138 | ``` 139 | private void newSingleThreadExecutor() { 140 | ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); 141 | for (int i = 1; i <= number; i++) { 142 | final int index = i; 143 | singleThreadPool.execute(new Runnable() { 144 | @Override 145 | public void run() { 146 | String threadName = Thread.currentThread().getName(); 147 | Log.v("潇湘剑雨", "线程:"+threadName+",正在执行第" + index + "个任务"); 148 | try { 149 | Thread.sleep(1000); 150 | } catch (InterruptedException e) { 151 | e.printStackTrace(); 152 | } 153 | } 154 | }); 155 | } 156 | } 157 | ``` 158 | 159 | 160 | - **3.3.2 打印日志如下** 161 | - 改了线程池的实现方式,即依次一个一个的处理任务,而且都是复用一个线程,日志为 162 | ![image](https://upload-images.jianshu.io/upload_images/4432347-5ad122ee9d55f409.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 163 | 164 | 165 | 166 | #### 3.4 newCachedThreadPool的使用 167 | - **3.4.1 创建一个newCachedThreadPool线程池** 168 | ``` 169 | private void newCachedThreadPool() { 170 | ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); 171 | for (int i = 1; i <= number; i++) { 172 | final int index = i; 173 | try { 174 | Thread.sleep(1000); 175 | } catch (InterruptedException e) { 176 | e.printStackTrace(); 177 | } 178 | cachedThreadPool.execute(new Runnable() { 179 | @Override 180 | public void run() { 181 | String threadName = Thread.currentThread().getName(); 182 | Log.v("潇湘剑雨newCachedThreadPool", "线程:" + threadName + ",正在执行第" + index + "个任务"); 183 | try { 184 | long time = index * 500; 185 | Thread.sleep(time); 186 | } catch (InterruptedException e) { 187 | e.printStackTrace(); 188 | } 189 | } 190 | }); 191 | } 192 | } 193 | ``` 194 | 195 | 196 | - **3.4.2 打印日志如下** 197 | - 为了体现该线程池可以自动根据实现情况进行线程的重用,而不是一味的创建新的线程去处理任务,我设置了每隔1s去提交一个新任务,这个新任务执行的时间也是动态变化的,所以,效果为:[博客](https://github.com/yangchong211/YCBlogs) 198 | ![image](https://upload-images.jianshu.io/upload_images/4432347-54da4f08327cca18.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 199 | 200 | 201 | #### 3.5 newScheduledThreadPool的使用 202 | - **3.5.1 创建一个newScheduledThreadPool线程池** 203 | ``` 204 | private void newScheduledThreadPool() { 205 | ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); 206 | //延迟2秒后执行该任务 207 | scheduledThreadPool.schedule(new Runnable() { 208 | @SuppressLint("LongLogTag") 209 | @Override 210 | public void run() { 211 | String threadName = Thread.currentThread().getName(); 212 | Log.e("潇湘剑雨newScheduledThreadPool", "线程:" + threadName + ",正在执行"); 213 | } 214 | }, 2, TimeUnit.SECONDS); 215 | //延迟1秒后,每隔2秒执行一次该任务 216 | scheduledThreadPool.scheduleAtFixedRate(new Runnable() { 217 | @Override 218 | public void run() { 219 | String threadName = Thread.currentThread().getName(); 220 | Log.e("潇湘剑雨", "线程:" + threadName + ",正在执行"); 221 | } 222 | }, 1, 2, TimeUnit.SECONDS); 223 | } 224 | ``` 225 | 226 | - **3.5.2 打印日志如下** 227 | - 通过日志可以发现schedule方法的任务只是执行了一次,然后每隔2秒执行一次该scheduleAtFixedRate方法中的任务 228 | ![image](https://upload-images.jianshu.io/upload_images/4432347-859e1af04c62dc09.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 229 | 230 | 231 | #### 3.6 线程创建规则 232 | - ThreadPoolExecutor对象初始化时,不创建任何执行线程,当有新任务进来时,才会创建执行线程。构造ThreadPoolExecutor对象时,需要配置该对象的核心线程池大小和最大线程池大小 233 | - 1. 当目前执行线程的总数小于核心线程大小时,所有新加入的任务,都在新线程中处理。[博客](https://github.com/yangchong211/YCBlogs) 234 | - 2. 当目前执行线程的总数大于或等于核心线程时,所有新加入的任务,都放入任务缓存队列中。 235 | - 3. 当目前执行线程的总数大于或等于核心线程,并且缓存队列已满,同时此时线程总数小于线程池的最大大小,那么创建新线程,加入线程池中,协助处理新的任务。 236 | - 4. 当所有线程都在执行,线程池大小已经达到上限,并且缓存队列已满时,就rejectHandler拒绝新的任务。 237 | 238 | 239 | ### 4.线程池封装 240 | #### 4.1 具体可以参考下篇文章 241 | 242 | 243 | #### 4.2 参考博客 244 | - Android性能优化之使用线程池处理异步任务:https://blog.csdn.net/u010687392/article/details/49850803 245 | 246 | 247 | 248 | ### 其他介绍 249 | #### 01.关于博客汇总链接 250 | - 1.[技术博客汇总](https://www.jianshu.com/p/614cb839182c) 251 | - 2.[开源项目汇总](https://blog.csdn.net/m0_37700275/article/details/80863574) 252 | - 3.[生活博客汇总](https://blog.csdn.net/m0_37700275/article/details/79832978) 253 | - 4.[喜马拉雅音频汇总](https://www.jianshu.com/p/f665de16d1eb) 254 | - 5.[其他汇总](https://www.jianshu.com/p/53017c3fc75d) 255 | 256 | 257 | 258 | #### 02.关于我的博客 259 | - github:https://github.com/yangchong211 260 | - 知乎:https://www.zhihu.com/people/yczbj/activities 261 | - 简书:http://www.jianshu.com/u/b7b2c6ed9284 262 | - csdn:http://my.csdn.net/m0_37700275 263 | - 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/ 264 | - 开源中国:https://my.oschina.net/zbj1618/blog 265 | - 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1 266 | - 邮箱:yangchong211@163.com 267 | - 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV 268 | - segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles 269 | - 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e 270 | 271 | 272 | --------------------------------------------------------------------------------