gpt4 book ai didi

Java、线程和优先级

转载 作者:行者123 更新时间:2023-12-02 11:02:22 26 4
gpt4 key购买 nike

我正在解决一个问题,本来应该很容易解决,但我并没有那么容易完成它。

问题非常简单:我有一个在 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;
}
}

最佳答案

您似乎遇到了一些问题。我将尝试总结它们并为前进的道路提供起点:

线程争用

使用BlockingQueue带有成本 - 每个写入操作(放入和取出)都是生产者或消费者之间的锁竞争。您的“A 池”有 9 个线程争夺 queueA 的写锁(1 个生产者,8 个消费者),而您的“B 池”有 3 个线程争夺 queueB 的锁(1 个生产者,2 个消费者)。

This related answer提供有关争用的更多详细信息。解决这个问题最简单的方法是“使用更少的线程”或使用“无锁”机制来消除争用。

线程调度

正如评论中提到的,您受到 JVM 如何调度线程的支配。

如果 java 线程调度在 CPU 上使用完全公平的时间共享,您可能会看到同一池中每个线程的消耗计数彼此非常接近。您可能已经注意到它们不是 - 我运行您的(稍作修改的)代码偶尔会在线程中得到 300K 或更多的计数分布。

当每个 CPU 绑定(bind)线程有足够的 CPU 核心(示例代码中有 12 个)时,通常可以得到更好的结果,但在许多情况下,它远非理想,尤其是在面临线程争用的情况下。

你能做什么?

  1. 构建您自己的公平逻辑 - 不要依赖 JVM 线程调度程序来保证公平,因为它不会公平。
    • 对于您的情况,一个简单的想法是保留两个队列,但使用单个池来处理两个队列 - 使用循环法或 Math.random() (即: if (rand < 0.8) { queueA.poll();} )来确定从哪个队列进行轮询。 注意 - 使用poll这样您就可以轻松处理队列为空的情况而不会发生阻塞。
  2. 对硬件上运行的 CPU 绑定(bind)线程数进行实验。根据我对上面 (1) 的建议,您甚至可以只有一个工作线程公平地处理两个队列。请记住,过多的线程争夺相同的资源会减慢处理速度。

线程不是很有趣吗? :)

关于Java、线程和优先级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51227241/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com