Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/361.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在多个java线程之间共享数据并获取更新的值_Java_Multithreading_Thread Safety - Fatal编程技术网

在多个java线程之间共享数据并获取更新的值

在多个java线程之间共享数据并获取更新的值,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我想创建一个java应用程序,我们想在访问令牌的帮助下为多个用户进行rest调用。我每个用户使用1个线程。我正在使用的访问令牌有效期为1小时。一旦令牌过期,我将收到401错误,必须更新所有线程的令牌,然后继续。我正在考虑使用一个可变变量,我已经使其成为静态变量来更新所有线程。我的要求是,当我在其中一个线程中知道令牌已过期时,我希望所有线程停止处理并等待新令牌生成(这需要几秒钟)。此外,一旦生成,令牌应自动更新,而不会因为令牌过期而使每个线程失败 下面是我编写的示例代码: import java.

我想创建一个java应用程序,我们想在访问令牌的帮助下为多个用户进行rest调用。我每个用户使用1个线程。我正在使用的访问令牌有效期为1小时。一旦令牌过期,我将收到401错误,必须更新所有线程的令牌,然后继续。我正在考虑使用一个可变变量,我已经使其成为静态变量来更新所有线程。我的要求是,当我在其中一个线程中知道令牌已过期时,我希望所有线程停止处理并等待新令牌生成(这需要几秒钟)。此外,一旦生成,令牌应自动更新,而不会因为令牌过期而使每个线程失败

下面是我编写的示例代码:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Sample {

public static void main(String[] args) {

    String[] myStrings = { "User1" , "User2" , "User3" };

    ScheduledExecutorService scheduledExecutorService = Executors
            .newScheduledThreadPool(myStrings.length);

    TokenGenerator.getToken();

    for(String str : myStrings){
        scheduledExecutorService.scheduleAtFixedRate(new Task(str), 0, 5, TimeUnit.SECONDS);    
    }
}

}

 class Task implements Runnable{

private String name;

public Task(String name){
    this.name = name;

}


@Override
public void run() {

    getResponse(TokenGenerator.token);

}

private void getResponse(String token) {
    // Make http calls
    // if token expire , call getToken again. Pause all the running threads , and
    // update the token for all threads

    TokenGenerator.getToken();
}

 }

 class TokenGenerator {

public static volatile String token;

public static void getToken() {

    token = "new Token everytime";

}

 }

有没有更好的办法解决这个问题?上面的代码不符合我的用例,因为一旦一个线程开始生成一个新的令牌,所有其他线程都不会被暂停。请求提出一些改进建议。

您可以使用以下模式,仅使用其getter访问令牌,并在收到错误响应时调用
loadToken

class TokenGenerator {
    private String token = null;
    public synchronized String getToken() {
        if (token == null) {
            loadToken();
        }
        return token;
    }
    public synchronized void loadToken() {
        token = "load here";
    }           
}
要解决暂停线程的问题,只要调用
getToken()
,就可以在任何需要暂停
线程的地方停止该线程,如果加载令牌当前处于活动状态,该线程将自动阻止

class Task implements Runnable{
    private String name;
    private TokenGenerator tokenGenerator;

    public Task(String name, TokenGenerator tokenGenerator) {
        this.name = name;
        this.tokenGenerator = tokenGenerator;
    }

    @Override
    public void run() {
        getResponse(tokenGenerator.getToken());
    }

    private void getResponse(String token) {
        // Make http calls
        // if token expire , call getToken again. Pause all the running threads , and
        // update the token for all threads

        tokenGenerator.loadToken();
    }
}

您可以将令牌放入并使用暂停线程:

public class TokenWrapper {
  private final AtomicReference<Token> tokenRef = new AtomicReference<>(null);
  private final Semaphore semaphore = new Semaphore(Integer.MAX_VALUE);

  public TokenWrapper() {
    Token newToken = // refresh token
    tokenRef.set(newToken);
  }

  public Token getToken() {
    Token token = null;
    while((token = tokenRef.get()) == null) {
      semaphore.acquire();
    }
    return token;
  }

  public Token refreshToken(Token oldToken) {
    if(tokenRef.compareAndSet(oldToken, null)) {
      semaphore.drainPermits();          
      Token newToken = // refresh token
      tokenRef.set(newToken);
      semaphore.release(Integer.MAX_VALUE);
      return newToken;
    } else return getToken();
  }
}

public class RESTService {
  private static final TokenWrapper tokenWrapper = new TokenWrapper();

  public void run() {
    Token token = tokenWrapper.getToken();
    Response response = // call service with token
    if(response.getStatus == 401) {
      tokenWrapper.refreshToken(token);
    }
  }
}
公共类令牌包装器{
private final AtomicReference tokenRef=新的AtomicReference(null);
私有最终信号量信号量=新信号量(Integer.MAX_值);
公共令牌包装器(){
令牌newToken=//刷新令牌
tokenRef.set(newToken);
}
公共令牌getToken(){
令牌=null;
而((token=tokenRef.get())==null){
semaphore.acquire();
}
返回令牌;
}
公共令牌刷新令牌(令牌旧令牌){
if(tokenRef.compareAndSet(oldToken,null)){
semaphore.drainPermits();
令牌newToken=//刷新令牌
tokenRef.set(newToken);
信号量释放(整数最大值);
返回纽顿;
}否则返回getToken();
}
}
公共类服务{
私有静态final-TokenWrapper-TokenWrapper=new-TokenWrapper();
公开募捐{
Token-Token=tokenWrapper.getToken();
Response=//使用令牌调用服务
如果(response.getStatus==401){
刷新令牌(token);
}
}
}
refreshttoken()
使用
tokenRef
上的原子
compareAndSet
来确保只有一个线程将刷新令牌,然后调用
信号灯上的
drainPermits()
以使其他线程等待令牌刷新
getToken()
如果标记不是
null
,则返回该标记,否则等待
信号量
——这是在循环中完成的,因为线程可能必须在
tokenRef
设置为
null
信号量
上调用
drainPermits()
之间旋转几个周期



编辑:修改了
refreshToken(Token oldToken)
的签名,以便传入旧令牌,而不是在方法内部读取-这是为了防止出现RESTService_a刷新令牌,RESTService_B使用旧过期令牌获取401的情况,然后RESTService_B在RESTService_A对
refreshttoken
的调用完成后调用
refreshttoken
,导致令牌被刷新两次。使用新签名,RESTService_B将传入旧的过期令牌,因此当旧令牌与新令牌不匹配时,
compareAndSet
调用将失败,从而导致
refreshToken
只被调用一次。

因为您需要做两件事(http调用和更新令牌),您可以尝试双向检查器

一个是检查令牌
是否过期
,另一个是检查是否有其他线程试图
更新令牌

这里用一个小代码来演示这个想法(它有点脏,所以可能需要一些清理)


如果我错了,请纠正我,伙计,你基本上希望所有线程等待其中一个刷新你的令牌,对吗?是的..这正是我想要的。。。如果其中一个线程确实获得了一个新的令牌,我希望所有线程的该值都被更新,而不管单个线程执行在哪里,它不应该尝试使用旧令牌并再次失败和创建令牌…非常感谢..这使设计更好…我将尝试对此进行更多改进。这正是我正在寻找的解决方案类型,这是我不容易想到的…非常感谢…非常感谢先生…你已经完全解决了我的问题。。。这对我来说太完美了:)如果正在执行refreshToken的线程抛出错误,其他线程将如何被确认,应该如何处理它们。
...
private string token
private volatile static isTokenExpired=false   //checking if the token is expired or not
private volatile static waitingForTokenRefresher=false;  //checking if we should wait for update.

@Override
public void run(){
  while(tokenisExpired){
      //wait
  }

  //http calls find out if token is good to go
  //check if no one else uses the token:
  if( token is actually expired){
   if(!waitingForTokenRefresher){
       isTokenExpired=true;
       waitingForTokenRefresher=true;
       //refresh token
       waitingForTokenRefresher=false
       isTokenExpired=false;
   }
  }
   while(!waitingForTokenRefresher){
     //wait...
   }

}