【发布时间】:2018-07-07 22:01:51
【问题描述】:
我正在解决一个问题,这应该很容易解决,但是我没有那么容易解决。
问题很简单:我有一个在 Linux/x86 上运行的 Java 程序,它可以执行两个基本功能 F1 和 F2。我想将 F1 设置为具有更高的优先级,即使 F2 不时执行是必须的,即队列中有 F1 请求的事实不能让 F2 请求永远等待。
虽然我的第一个队列只是为每个功能设置一个带有线程池的单独队列,但我将 F1 池设置为有 8 个线程,而 F2 池只有 2 个线程。
在我的预期中,Linux 会为每个线程提供相当的时间共享,因此 F1 将有 8 个量子,而 F2 将只有 2 个。如果没有 F1 请求,F2 池可以将每个量子都分配给它自己,这应该也是如此用于 F1 以防 F2 没有请求。
但是,程序的行为并非如此,如果我收到一连串 F2 请求,而只有几个 F1 请求,则后者需要很长时间才能轮到它。
谈论 Oracle HotSpot/linux 调度是否有意义?或者它不应该发生,从我的角度来看,什么会指向一个实施错误?
PS:我已经阅读了有关 linux 调度的信息,似乎 SCHED_OTHER (TS) 为每个任务提供了时间共享,但是每次未执行准备好的任务时,它都会获得更大的时间量,如果这种情况发生在 F2池,这可能解释了上述行为。
感谢和问候。
下面有一个示例源代码。
package test;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
/**
* Created by giscardff on 08/07/18.
*/
public class TestThread {
// Test Program
public static void main(String args[]) throws Exception {
// queues containing jobs to be done
ArrayBlockingQueue<MyDTO> queueA = new ArrayBlockingQueue<>(100);
ArrayBlockingQueue<MyDTO> queueB = new ArrayBlockingQueue<>(100);
// create pool for functionality A
for(int i = 1; i <= 8; i++){
MyThread thread = new MyThread("ThreadA" + i, queueA);
thread.start();
}
// create pool for functionality B
for(int i = 1; i <= 2; i++){
MyThread thread = new MyThread("ThreadB" + i, queueB);
thread.start();
}
// create producer for A
// it will take 100ms between requests
Producer producerA = new Producer(queueA, 0);
producerA.start();
// create producer for B
// it will take 0ms between requests
Producer producerB = new Producer(queueB, 0);
producerB.start();
}
}
/**
* Just put a request into a queue
*/
class Producer extends Thread {
private ArrayBlockingQueue<MyDTO> queue;
private long sleep;
public Producer(ArrayBlockingQueue<MyDTO> queue, long sleep){
this.queue = queue;
this.sleep = sleep;
}
@Override
public void run() {
try {
while (true) {
if(sleep > 0)Thread.sleep(sleep);
queue.put(new MyDTO());
}
}catch(Exception ex){}
}
}
/**
* Retrieve a request from a queue, calculate how long request took to
* be received for each 1M requests
*/
class MyThread extends Thread {
private ArrayBlockingQueue<MyDTO> queue;
private long delay = 0;
private int count = 0;
public MyThread(String name, ArrayBlockingQueue<MyDTO> queue){
super(name);
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
MyDTO input = queue.take();
delay += System.currentTimeMillis() - Long.parseLong(input.getTime());
if(++count % 1000 == 0){
System.out.printf("%s: %d\n", getName(), delay / 10);
count = 0;
}
}
}catch(Exception ex){ex.printStackTrace();}
}
}
/**
* Just a DTO representing a request
* NOTE: The time was set as String to force CPU to do something more than just math operations
*/
class MyDTO {
private String time;
public MyDTO(){
this.time = "" + System.currentTimeMillis();
}
public String getTime() {
return time;
}
}
【问题讨论】:
-
我认为您受制于操作系统调度程序以及 JVM 与它的交互方式。我不知道有任何 Java API 可以让您精确设置两个线程分配时间的确切时间量。
-
您能发布管理线程的代码吗?任务是 IO 受限还是 CPU 受限?你是如何测量它们的?
-
机器有几个核心?
-
我认为解决此类问题的最佳方法是 PriorityQueue,或者可能是 ForkJoinPool,因为它的工作窃取算法。
-
您的代码正在运行 SQL 语句?然后您就没有使用 CPU,因为它们大部分时间都在等待数据库服务器。您的性能测试毫无意义,因为您根本没有测试应用服务器的 CPU 调度。
标签: java multithreading scheduling