Java (Android) 多线程进程
Java (Android) multi-threading process
我正在开发创建多线程的应用程序(Matt's traceroute windows 版本 http://winmtr.net/),每个线程都有自己的进程(执行 ping 命令). ThreadPoolExecutor
一段时间后(例如 10 秒)关闭所有线程
ThreadPoolExecutor
使用阻塞队列(在任务执行前保留任务)
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, NUMBER_OF_CORES * 2 + 2, 10L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
);
PingThread.java
private class PingThread extends Thread {
@Override
public void run() {
long pingStartedAt = System.currentTimeMillis();
// PingRequest is custom object
PingRequest request = buildPingRequest(params);
if (!isCancelled() && !Thread.currentThread().isInterrupted()) {
// PingResponse is custom object
// Note:
// executePingRequest uses PingRequest to create a command
// which than create a runtime process to execute ping command
// using string response i am creating PingResponse
PingResponse pingResponse = PingUtils.executePingRequest(request);
if (pingResponse != null) {
pingResponse.setHopLocation(hopLocation);
// publish ping response to main GUI/handler
publishProgress(pingResponse);
} else
Logger.error(
"PingThread", "PingResponse isNull for " + request.toString()
);
}
}
}
现在如果我创建多个线程,比如在一个循环中超过 500 个并在池执行器中执行
正在执行线程
PingThread thread = new PingThread(params);
poolExecutor.execute(thread);
我知道 LinkedBlockingQueue
在执行任务之前会保留任务。每个线程的进程最多需要 200 到 400 毫秒,但通常小于 10 毫秒
我在做什么
for (int iteration = 1; iteration <= 50/*configurable*/; iteration++) {
for (int index = 0; index < 10/*configurable*/; index++) {
PingThread thread = new PingThread(someParams);
poolExecutor.execute(thread);
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Logger.error(false, e);
}
}
50 次迭代大约需要 25 秒,这里我只有最多 40 个 ping 响应,其余视为由于超时而丢失。如果我增加迭代损失也会增加(由于线程数增加呈指数增长)
观察:
我是 运行 Galaxy S6 上的这个应用程序,它有 8 个内核,应用程序池大小为 16,最大池大小为 16 + 2,我知道处理器一次只运行一个线程,它共享并行处理的量子时间。
通过及时观察ThreadPoolExecutor
,我看到队列中有很多任务,由于LinkedBlockingQueue
,超时后队列中仍有许多线程存在
如果我减少线程数它工作正常但如果增加它会产生问题
问题:
- 当我使用带有双核处理器的设备时,Ping 响应会减少。
- 为什么队列中有很多线程,每个线程占用
大约 10 到 50 毫秒(增加线程时间将增加 uptp 300 毫秒或
更多)?
- 它应该在给定的时间内完成,为什么没有?
- 如何克服这个问题?
- 我应该使用
ConcurrentLinkedQueue
但它使用 Producer/consumer 模型,不知何故 ThreadPoolExecutor
(我认为是)也使用此模型。
LinkedBlockingQueue
在任务执行之前持有任务(线程空闲或在队列中),如何克服这个问题?
- 通过为后面的迭代设置
Thread.MAX_PRIORITY
并不能解决问题(后面的迭代线程在队列中)
- 减少线程数可以解决问题,为什么?因为队列中没有线程?
- 有什么方法可以检查,如果队列中的线程接受它们然后执行其他线程,而不阻塞其他线程但在给定时间内。
- 增加 5 秒这样的额外时间不是解决方案
- 像 How to get the ThreadPoolExecutor to increase threads to max before queueing? 中那样更改
corePoolSize
在我的情况下不起作用。
在测试期间,内存和处理器使用量在限制范围内。
需要详细信息 answer/help。
编辑
当应用程序进入后台时,没有任何损失,用户 CPU 使用率下降到 0-2%,而焦点应用程序占用 cpu 使用率的 4-6%。是不是因为 UI 和其他 ralted 的东西,我试图删除所有不必要的代码我也确实将 PingThread
更改为 PingTask
PingTask implements Runnable {/*....*/}
注:
我使用相同的代码创建了单独的基于 java 的应用程序,它在桌面上运行良好,所以我们可以说这是 android OS 特定问题吗?
我不确定这是否是导致所有问题的原因,但您正在创建 很多 不必要的线程。
你应该更换
private class PingThread extends Thread {
与 :
private class PingThread implements Runnable {
或(使用更合适的名称):
private class PingTask implements Runnable {
即提交给 Executor
s 的任务本身不应该是线程。它有效,因为 Thread
实现了 Runnable
,但你在浪费它。
线程创建一个新的唯一对象,而runnable允许所有线程共享一个对象。因此,在尝试多线程时不应扩展 Thread,而应使用 Runnable:
class RunnableDemo implements Runnable {
private Thread thread;
String threadName="My thread";
public void run() {
//do your code from here 'logic'
System.out.println("Threading is Running");
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: "+threadName +" "+ i);
// Let the thread sleep for a while.
Thread.sleep(50); //sleep your content for xx miliseconds
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
//finish your work here
}
public void start () {
System.out.println("Starting " + threadName );
if (thread == null) {
thread = new Thread (this);
thread.start (); //This will call your run methods of Runnable
}
}
}
//test your thread from here
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
观察:
在使用相同的代码创建并观察独立的 java 应用程序(日志)后,我了解到以下内容:
- 不知何故 android OS 架构 and/or 处理器正在限制线程数。
LinkedBlockingQueue
在任务执行之前保留任务,因此如果队列很长,队列中的后续线程将不得不等待更多时间。
- Increase/Dynamic
corePoolSize
和 maxPoolSize
也在做同样的事情,他们在队列 中添加了线程
- 应用程序使用了 CPU 的 4-6%,所以我们不能说 CPU 正在超载或使用全部应用程序资源,但是当应用程序进入后台(GUI 或其他相关 OS and/or 基于应用程序的线程可能会停止或中断)CPU 使用下降到 0-3%。
解决方案:
对于 50 次迭代和 10 次内部创建 500 个线程,现在我做了两件事:
- 增加
Thread.sleep(millis)
时间并涉及一些计算。
- 减少每次迭代的线程数。我现在正在创建 10 个线程
Math.ceil((double) 10 / 3) = 3
所以我们有 3 个顺序 PingUtils.executePingRequest(pingRequest)
每个线程即 3 * 3 = 9
仍然是 1 所以我们将为最后一个请求创建一个单独的线程。对于每次迭代而不是创建 10 个线程,我现在创建 4 个线程。
- 现在使用这种方法我有 200 个线程而不是 500 个线程,这解决了问题。
我正在开发创建多线程的应用程序(Matt's traceroute windows 版本 http://winmtr.net/),每个线程都有自己的进程(执行 ping 命令). ThreadPoolExecutor
一段时间后(例如 10 秒)关闭所有线程
ThreadPoolExecutor
使用阻塞队列(在任务执行前保留任务)
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, NUMBER_OF_CORES * 2 + 2, 10L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
);
PingThread.java
private class PingThread extends Thread {
@Override
public void run() {
long pingStartedAt = System.currentTimeMillis();
// PingRequest is custom object
PingRequest request = buildPingRequest(params);
if (!isCancelled() && !Thread.currentThread().isInterrupted()) {
// PingResponse is custom object
// Note:
// executePingRequest uses PingRequest to create a command
// which than create a runtime process to execute ping command
// using string response i am creating PingResponse
PingResponse pingResponse = PingUtils.executePingRequest(request);
if (pingResponse != null) {
pingResponse.setHopLocation(hopLocation);
// publish ping response to main GUI/handler
publishProgress(pingResponse);
} else
Logger.error(
"PingThread", "PingResponse isNull for " + request.toString()
);
}
}
}
现在如果我创建多个线程,比如在一个循环中超过 500 个并在池执行器中执行
正在执行线程
PingThread thread = new PingThread(params);
poolExecutor.execute(thread);
我知道 LinkedBlockingQueue
在执行任务之前会保留任务。每个线程的进程最多需要 200 到 400 毫秒,但通常小于 10 毫秒
我在做什么
for (int iteration = 1; iteration <= 50/*configurable*/; iteration++) {
for (int index = 0; index < 10/*configurable*/; index++) {
PingThread thread = new PingThread(someParams);
poolExecutor.execute(thread);
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Logger.error(false, e);
}
}
50 次迭代大约需要 25 秒,这里我只有最多 40 个 ping 响应,其余视为由于超时而丢失。如果我增加迭代损失也会增加(由于线程数增加呈指数增长)
观察:
我是 运行 Galaxy S6 上的这个应用程序,它有 8 个内核,应用程序池大小为 16,最大池大小为 16 + 2,我知道处理器一次只运行一个线程,它共享并行处理的量子时间。
通过及时观察ThreadPoolExecutor
,我看到队列中有很多任务,由于LinkedBlockingQueue
如果我减少线程数它工作正常但如果增加它会产生问题
问题:
- 当我使用带有双核处理器的设备时,Ping 响应会减少。
- 为什么队列中有很多线程,每个线程占用 大约 10 到 50 毫秒(增加线程时间将增加 uptp 300 毫秒或 更多)?
- 它应该在给定的时间内完成,为什么没有?
- 如何克服这个问题?
- 我应该使用
ConcurrentLinkedQueue
但它使用 Producer/consumer 模型,不知何故ThreadPoolExecutor
(我认为是)也使用此模型。 LinkedBlockingQueue
在任务执行之前持有任务(线程空闲或在队列中),如何克服这个问题?- 通过为后面的迭代设置
Thread.MAX_PRIORITY
并不能解决问题(后面的迭代线程在队列中) - 减少线程数可以解决问题,为什么?因为队列中没有线程?
- 有什么方法可以检查,如果队列中的线程接受它们然后执行其他线程,而不阻塞其他线程但在给定时间内。
- 增加 5 秒这样的额外时间不是解决方案
- 像 How to get the ThreadPoolExecutor to increase threads to max before queueing? 中那样更改
corePoolSize
在我的情况下不起作用。
在测试期间,内存和处理器使用量在限制范围内。
需要详细信息 answer/help。
编辑
当应用程序进入后台时,没有任何损失,用户 CPU 使用率下降到 0-2%,而焦点应用程序占用 cpu 使用率的 4-6%。是不是因为 UI 和其他 ralted 的东西,我试图删除所有不必要的代码我也确实将 PingThread
更改为 PingTask
PingTask implements Runnable {/*....*/}
注: 我使用相同的代码创建了单独的基于 java 的应用程序,它在桌面上运行良好,所以我们可以说这是 android OS 特定问题吗?
我不确定这是否是导致所有问题的原因,但您正在创建 很多 不必要的线程。
你应该更换
private class PingThread extends Thread {
与 :
private class PingThread implements Runnable {
或(使用更合适的名称):
private class PingTask implements Runnable {
即提交给 Executor
s 的任务本身不应该是线程。它有效,因为 Thread
实现了 Runnable
,但你在浪费它。
线程创建一个新的唯一对象,而runnable允许所有线程共享一个对象。因此,在尝试多线程时不应扩展 Thread,而应使用 Runnable:
class RunnableDemo implements Runnable {
private Thread thread;
String threadName="My thread";
public void run() {
//do your code from here 'logic'
System.out.println("Threading is Running");
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: "+threadName +" "+ i);
// Let the thread sleep for a while.
Thread.sleep(50); //sleep your content for xx miliseconds
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
//finish your work here
}
public void start () {
System.out.println("Starting " + threadName );
if (thread == null) {
thread = new Thread (this);
thread.start (); //This will call your run methods of Runnable
}
}
}
//test your thread from here
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
观察:
在使用相同的代码创建并观察独立的 java 应用程序(日志)后,我了解到以下内容:
- 不知何故 android OS 架构 and/or 处理器正在限制线程数。
LinkedBlockingQueue
在任务执行之前保留任务,因此如果队列很长,队列中的后续线程将不得不等待更多时间。- Increase/Dynamic
corePoolSize
和maxPoolSize
也在做同样的事情,他们在队列 中添加了线程
- 应用程序使用了 CPU 的 4-6%,所以我们不能说 CPU 正在超载或使用全部应用程序资源,但是当应用程序进入后台(GUI 或其他相关 OS and/or 基于应用程序的线程可能会停止或中断)CPU 使用下降到 0-3%。
解决方案:
对于 50 次迭代和 10 次内部创建 500 个线程,现在我做了两件事:
- 增加
Thread.sleep(millis)
时间并涉及一些计算。 - 减少每次迭代的线程数。我现在正在创建 10 个线程
Math.ceil((double) 10 / 3) = 3
所以我们有 3 个顺序PingUtils.executePingRequest(pingRequest)
每个线程即3 * 3 = 9
仍然是 1 所以我们将为最后一个请求创建一个单独的线程。对于每次迭代而不是创建 10 个线程,我现在创建 4 个线程。 - 现在使用这种方法我有 200 个线程而不是 500 个线程,这解决了问题。