我希望在向外部系统发送请求时实现严格的轮询调度。有两个外部系统服务器,第一个请求应该发送到“System1”,第二个请求必须发送到“System2”,下一个请求再次发送到“System1”以此类推。
由于我只有两个服务器可以发送请求,并且希望获得最大的性能而不会出现任何阻塞和上下文切换,因此我选择了 AtomicBoolean,因为它利用了 CAS 操作。
我的实现类如下:
1. RoundRobinTest.java
由于我只有两个服务器可以发送请求,并且希望获得最大的性能而不会出现任何阻塞和上下文切换,因此我选择了 AtomicBoolean,因为它利用了 CAS 操作。
我的实现类如下:
1. RoundRobinTest.java
package com.concurrency;
import java.util.Iterator;
public class RoundRobinTest
{
public static void main(String[] args)
{
for (int i = 0; i < 500; i++)
{
new Thread(new RoundRobinLogic()).start();
}
try
{
// Giving a few seconds for the threads to complete
Thread.currentThread().sleep(2000);
Iterator<String> output = RoundRobinLogic.output.iterator();
int i=0;
while (output.hasNext())
{
System.out.println(i+++":"+output.next());
// Sleeping after each out.print
Thread.currentThread().sleep(20);
}
}
catch (Exception ex)
{
// do nothing
}
}
}
2. RoundRobinLogic.java(带有静态AtomicBoolean对象的类)
package com.concurrency;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;
public class RoundRobinLogic implements Runnable
{
private static AtomicBoolean bool = new AtomicBoolean(true);
public static Queue<String> output = new ConcurrentLinkedDeque<>();
@Override
public void run()
{
if(bool.getAndSet(false))
{
// Sending the request to first system
output.add("Request to System1");
}
else if(!bool.getAndSet(true))
{
// Sending the request to first system
output.add("Request to System2");
}
}
}
输出:
......................
314:Request to System1
315:Request to System2
316:Request to System1
317:Request to System2
318:Request to System1
319:Request to System1
320:Request to System2
321:Request to System2
322:Request to System1
323:Request to System2
324:Request to System1
325:Request to System2
......................
请求318和319已经发送到同一台服务器,此时AtomicBoolean失效。对于我的应用程序,1000-2000个线程可能同时访问共享对象。 从《Java并发实践》中,我看到了以下内容。
在高争用级别下,锁定倾向于优于原子变量,但在更现实的争用级别下,原子变量优于锁定。这是因为锁定通过暂停线程来响应争用,减少CPU使用率和共享内存总线上的同步流量。 对于低到中等争用,原子可扩展性更好;对于高争用,锁提供更好的避免竞争的能力。(基于CAS的算法也比基于锁的算法在单CPU系统上表现更好,因为除非线程在读取修改写入操作的中间被抢占,否则CAS始终成功。)
现在我有以下问题。
- 是否有其他有效的非阻塞方式来实现轮流发送请求?
- 在严重争用的情况下,AtomicBoolean是否会失败?我的理解是,由于重度争用,性能/吞吐量可能会下降。但在上面的示例中,AtomicBoolean失败了。为什么?
bool
和queue
的状态彼此依赖,但它们的访问并没有同步。 - Mick Mnemonicrun
方法没有利用 CAS。考虑这个问题:由于时间问题,你的run
方法中的两次对output.add
的调用都有可能不会被执行。 - Sean Bright