首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于AtomicBoolean的Java循环调度算法

基于AtomicBoolean的Java循环调度算法
EN

Stack Overflow用户
提问于 2015-04-13 18:49:32
回答 3查看 7.3K关注 0票数 7

当我向外部系统发送请求时,我希望实现严格的循环调度。有两个外部系统服务器。第一个请求应该转到'System1‘,第二个请求必须转到'System2’,下一个请求到'System1‘等等。

由于我只有两台服务器可以发送请求,而且我希望在没有任何阻塞和上下文切换的情况下获得最大性能,所以我选择了AtomicBoolean,因为它使用了CAS操作。

我的实现类

1. RoundRobinTest.java

代码语言:javascript
复制
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对象)

代码语言:javascript
复制
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");
        }       
    }

}

输出:

代码语言:javascript
复制
......................
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系统上成功,除非线程在读修改写入操作中被抢占。)

现在我有以下问题。

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

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-04-13 19:41:05

撇开John的回答不谈,一个更干净、或许更有效的RoundRobinLogic实现将使用AtomicIntegerAtomicLong。这样就不需要将AtomicBoolean的当前值与新值进行比较:

代码语言:javascript
复制
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");
        }
    }
}

这将使您可以很容易地将其扩展到其他系统:

代码语言:javascript
复制
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());
    }
}
票数 11
EN

Stack Overflow用户

发布于 2015-04-13 19:25:48

让我们假设您不是使用Queue,而是对实际系统使用api。我所看到的问题涉及:

代码语言:javascript
复制
    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是假的,但设置为真购买另一个线程呢?在这种情况下,两次尝试都将失败。

我会把这个重构成:

代码语言:javascript
复制
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同步的一部分。

票数 4
EN

Stack Overflow用户

发布于 2015-04-13 21:15:25

因为您的需求基本上是:实现一个原子操作

  1. 计算和翻转布尔值(或计算模块,并在通用n服务器情况下递增计数器)
  2. 根据步骤1的结果将条目插入队列,

通过使步骤1和步骤2单独线程安全并不能真正做到这一点;您必须将步骤1和步骤2同步在一起。

下面是一个应该有效的简单实现:

代码语言:javascript
复制
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;
        }
    }
}

关于你的问题:

  1. 如果需要同步两个高于处理器级别的操作,则无法避免阻塞。java.util.concurrent.atomic中的类使用机器级原子指令,这就是为什么使用这些类的代码(通常取决于平台)不需要阻塞。
  2. 在您的实现中,AtomicBoolean没有失败。相反,在读取布尔值和向队列中添加元素之间存在一个争用条件。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29612828

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档