不必要的Java上下文切换

时间:2009-11-18 21:16:05

标签: java multithreading context-switching

我有一个通过固定容量通道进行通信的Java线程(基于流程的编程)网络 - 在WindowsXP下运行。基于我们对“绿色”线程(非抢占式)的经验,我们所期望的是,如果通道变得更大,线程将更少地切换上下文(从而减少CPU时间)。但是,我们发现增加通道大小对运行时间没有任何影响。似乎正在发生的事情是Java决定切换线程,即使通道未满或空(即使线程不必挂起),这会花费CPU时间没有明显的优势。更改线程优先级也不会产生任何可观察到的差异。

我的问题是,是否有某种方法可以说服Java不要进行不必要的上下文切换,但是在切换线程之前必须暂停切换 - 是否有某种方法可以改变Java的调度逻辑?还是它对我没注意的事情做出反应?!或者是否存在其他异步机制,例如线程工厂,Runnable(s),甚至是守护进程(!)。答案似乎并不明显,因为到目前为止,我的通讯员都没有提出答案(包括最近两位CS教授)。或者也许我错过了一些非常明显的东西,人们无法想象我不知道它......

我在这里添加了发送和接收代码 - 不是很优雅,但似乎工作...... ;-)如果您想知道,我认为'send'中的goLock逻辑可能会导致问题,但暂时删除它并没有任何区别。我添加了发送和接收代码......

public synchronized Packet receive() {

if (isDrained()) {
    return null;
}

while (isEmpty()) {


  try {
    wait();
  } catch (InterruptedException e) {        
    close();
    return null;

  }     

  if (isDrained()) {

    return null;
  }

}

if (isDrained()) {     
  return null;
}
if (isFull()) {
  notifyAll(); // notify other components waiting to send
}
Packet packet = array[receivePtr];
array[receivePtr] = null;
receivePtr = (receivePtr + 1) % array.length;
//notifyAll(); // only needed if it was full
usedSlots--;

packet.setOwner(receiver);

if (null == packet.getContent()) {
  traceFuncs("Received null packet");
} else {
  traceFuncs("Received: " + packet.toString());
}


return packet;

}

synchronized boolean send(final Packet packet, final OutputPort op) {

sender = op.sender;

if (isClosed()) {

  return false;
}

while (isFull()) {


  try {
    wait();
  } catch (InterruptedException e) {        
    indicateOneSenderClosed();       
    return false;
  }

  sender = op.sender;

}

if (isClosed()) {

  return false;
}

try {
  receiver.goLock.lockInterruptibly();
} catch (InterruptedException ex) {
  return false;
}

try {
  packet.clearOwner();
  array[sendPtr] = packet;
  sendPtr = (sendPtr + 1) % array.length;
  usedSlots++; // move this to here
  if (receiver.getStatus() == StatusValues.DORMANT || receiver.getStatus() == StatusValues.NOT_STARTED) {
    receiver.activate(); // start or wake up if necessary
  } else {
    notifyAll(); // notify receiver
    // other components waiting to send to this connection may also get
    // notified,
    // but this is handled by while statement 
  }

  sender = null;
  Component.network.active = true;
} finally {
  receiver.goLock.unlock();

}
return true;

}

if (isDrained()) { return null; } while (isEmpty()) { try { wait(); } catch (InterruptedException e) { close(); return null; } if (isDrained()) { return null; } } if (isDrained()) { return null; } if (isFull()) { notifyAll(); // notify other components waiting to send } Packet packet = array[receivePtr]; array[receivePtr] = null; receivePtr = (receivePtr + 1) % array.length; //notifyAll(); // only needed if it was full usedSlots--; packet.setOwner(receiver); if (null == packet.getContent()) { traceFuncs("Received null packet"); } else { traceFuncs("Received: " + packet.toString()); } return packet;


感谢您的提问!我一直在Sun论坛上讨论同样的问题,这是我在该论坛上的最后一篇文章:

  

我们现在最好的猜测是这种效果来自Windows'   调度逻辑。

     

微软似乎承认这个领域需要一些改进   因为它引入了UMS - 我引用:“UMS推荐用于应用程序   具有高性能要求,需要高效运行许多   多处理器或多核系统上的线程并发。 ...... UMS是   从64位版本的Windows 7和Windows开始提供   Server 2008 R2。此功能在32位版本上不可用   Windows。“希望Java稍后会利用UMS   释放。

感谢您的帮助!

3 个答案:

答案 0 :(得分:4)

绿色线程消失了(也许Solaris支持它但我怀疑)。此外,Java不会切换线程,操作系统会这样做。 Java所做的唯一事情就是向操作系统发出信号,即线程通过使用OS函数进行空闲/等待/阻塞。因此,如果你的程序遇到任何同步点,Thread.wait / sleep会发出信号,它不再需要cpu了。

除此之外,操作系统维护时间片并将从线程中取走cpu,即使在其他线程等待cpu时它仍然可以运行。

你能在这里发布更多代码吗?

答案 1 :(得分:1)

我有点尴尬 - 今天下午我突然想到可能是我担心的表现太简单的网络,因为我只有两个进程 es ,两个进程的 ORS 即可。所以Windows可能一直在努力保持处理器的平衡!所以我想知道如果我给了Windows很多进程会发生什么。

我建立了两个网络:

a)50生成组件,提供50个Discard组件 - 即高度并行的网络 - 总共100个线程

b)50生成组件提供1个丢弃组件 - 即高度“漏斗状”网络 - 这样就是51个线程

我连续运行6次,连接容量为10次,连续容量为100次,每次运行6次。每次运行总共生成50 * 20,000个信息包,共计1,000,000个数据包,并运行约1个分钟..

以下是4个案例的平均值: a)连接容量为10 - 59.151秒。 a)连接容量为100 - 52.008秒。

b)连接容量为10 - 76.745秒。 b)连接容量为100 - 60.667秒。

所以看起来连接容量确实有所作为!并且,看起来JavaFBP的表现相当不错...我为有点草率道歉 - 但也许这让我们都对多核机器中的多线程思考得更深了......; - )

再次道歉,感谢所有为此话题做出贡献的人!

答案 2 :(得分:0)

很抱歉,如果这是完全虚假的,但我很确定Java自Java 1.1以来不再使用绿色线程了。至少Wikipedia也是如此。

这会限制您使用优先级 - 但在大多数情况下,我无法实现可观察到的性能改进。