使用AtomicBoolean的Java循环调度算法

Alb*_*bin 5 java algorithm multithreading atomicity atomicboolean

我想在向外部系统发送请求时实施严格的循环调度.有两个外部系统服务器.第一个请求应该转到'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
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

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");
        }       
    }

}
Run Code Online (Sandbox Code Playgroud)

输出:


......................
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
......................
Run Code Online (Sandbox Code Playgroud)

请求318和319已发送到同一服务器,AtomicBoolean在此方案中失败.对于我的应用程序,1000-2000个线程可能一次访问共享对象.从实践中的Java并发性,我已经看到了以下内容.

在高争用级别,锁定往往优于原子变量,但在更现实的争用级别,原子变量优于锁定.这是因为锁通过挂起线程来对争用作出反应,从而减少共享内存总线上的CPU使用率和同步流量. 由于低到中等的争用,原子提供了更好的可扩展性; 在高争用的情况下,锁可以提供更好的争用避免.(基于CAS的算法在单CPU系统上也优于基于锁定的算法,因为CAS总是在单个CPU系统上成功,除非在读取修改写入操作的中间线程被抢占的情况不太可能.)

现在我有以下问题.

  1. 是否有其他有效的非阻塞方式,实现循环请求发送.
  2. 在激烈争论下,AtomicBoolean有可能失败吗?我的理解是,由于争用很大,性能/吞吐量可能会下降.但在上面的例子中AtomicBoolean失败了.为什么?

Sea*_*ght 8

除了约翰的回答,一个更清洁,也许稍微更有效的实现RoundRobinLogic将使用AtomicIntegerAtomicLong.这消除了将当前值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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这将允许您相当容易地将其扩展到其他系统:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)