当我向外部系统发送请求时,我希望实现严格的循环调度。有两个外部系统服务器。第一个请求应该转到'System1‘,第二个请求必须转到'System2’,下一个请求到'System1‘等等。
由于我只有两台服务器可以发送请求,而且我希望在没有任何阻塞和上下文切换的情况下获得最大性能,所以我选择了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(Class (带有静态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使用和共享内存总线上的同步流量来响应争用。对于低到中度争用,atomics提供了更好的可伸缩性;对于高争用,锁提供了更好的争用避免。(在单CPU系统上,基于CAS的算法也优于基于锁的算法,因为CAS总是在单个CPU系统上成功,除非线程在读修改写入操作中被抢占。)
现在我有以下问题。
发布于 2015-04-13 19:41:05
撇开John的回答不谈,一个更干净、或许更有效的RoundRobinLogic实现将使用AtomicInteger或AtomicLong。这样就不需要将AtomicBoolean的当前值与新值进行比较:
class RoundRobinLogic implements Runnable
{
private static final AtomicInteger systemIndex = new AtomicInteger(1);
public static final Queue<String> output = new ConcurrentLinkedDeque<>();
@Override
public void run()
{
if (systemIndex.incrementAndGet() % 2 == 0) {
// Sending the request to first system
output.add("Request to System1");
} else {
// Sending the request to second system
output.add("Request to System2");
}
}
}这将使您可以很容易地将其扩展到其他系统:
class RemoteSystem
{
private final String name;
RemoteSystem(String name)
{
this.name = name;
}
public String name()
{
return name;
}
}
class RoundRobinLogic implements Runnable
{
private static final AtomicInteger systemIndex = new AtomicInteger(1);
private static final RemoteSystem[] systems = new RemoteSystem[] {
new RemoteSystem("System1"),
new RemoteSystem("System2"),
new RemoteSystem("System3"),
new RemoteSystem("System4"),
};
public static final Queue<String> output = new ConcurrentLinkedDeque<>();
@Override
public void run()
{
RemoteSystem system = systems[systemIndex.incrementAndGet() % systems.length];
// Sending the request to right system
output.add("Request to " + system.name());
}
}发布于 2015-04-13 19:25:48
让我们假设您不是使用Queue,而是对实际系统使用api。我所看到的问题涉及:
if(bool.getAndSet(false))
{
// Sending the request to first system
output.add("Request to System1");
}
else if(!bool.getAndSet(true))
{
// Sending the request to second system
output.add("Request to System2");
} 如果和条件都失败了怎么办?那件事怎么可能?想象一下,当进入第一个if时,bool是true。然后,尝试将其设置为false,但另一个线程会将您击败,因此您将看到false。然后,您可以尝试else if。现在,如果当您到达的else if是假的,但设置为真购买另一个线程呢?在这种情况下,两次尝试都将失败。
我会把这个重构成:
while(true){
boolean current = bool.get();
if(bool.compareAndSet(current, !current){
if(current){
//send request to first system
} else {
//send request to second system
}
return;
}
}正如Sean提到的,因为您要添加到一个队列中,即使像上面那样实现它,您仍然可能会看到一些无序的值,因为队列本身并不是与AtomicBoolean同步的一部分。
发布于 2015-04-13 21:15:25
因为您的需求基本上是:实现一个原子操作
n服务器情况下递增计数器)和通过使步骤1和步骤2单独线程安全并不能真正做到这一点;您必须将步骤1和步骤2同步在一起。
下面是一个应该有效的简单实现:
import java.util.LinkedList;
import java.util.Queue;
public class RoundRobinLogic implements Runnable
{
private static boolean bool = true;
public static final Queue<String> OUTPUT = new LinkedList<String>();
private static final Object LOCK = new Object();
@Override
public void run() {
synchronized (LOCK) {
OUTPUT.add(bool ? "Request to System1" : "Request to System2");
bool = !bool;
}
}
}关于你的问题:
java.util.concurrent.atomic中的类使用机器级原子指令,这就是为什么使用这些类的代码(通常取决于平台)不需要阻塞。AtomicBoolean没有失败。相反,在读取布尔值和向队列中添加元素之间存在一个争用条件。https://stackoverflow.com/questions/29612828
复制相似问题