Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/304.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 调用线程如何周期性地等待ScheduledExecutorService下的任务完成作业_Java_Multithreading_Executorservice_Scheduledexecutorservice - Fatal编程技术网

Java 调用线程如何周期性地等待ScheduledExecutorService下的任务完成作业

Java 调用线程如何周期性地等待ScheduledExecutorService下的任务完成作业,java,multithreading,executorservice,scheduledexecutorservice,Java,Multithreading,Executorservice,Scheduledexecutorservice,我有一个要求,比如在进行http调用后,必须每45分钟更新一次值 import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEnt

我有一个要求,比如在进行http调用后,必须每45分钟更新一次值

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class TokenManagerRunnable implements Runnable{
    
    String token;

    
    public String fetchToken() {
        return this.token;
    }
    

    @Override
    public void run() {
        
        String result = "";
        
        HttpPost post = new HttpPost("https://login.microsoftonline.com/{{TenentId}}/oauth2/token");
        List<NameValuePair> urlParameters = new ArrayList<>();
        urlParameters.add(new BasicNameValuePair("grant_type", "client_credentials"));
        urlParameters.add(new BasicNameValuePair("client_id", "some client id"));
        urlParameters.add(new BasicNameValuePair("client_secret", "some secret id"));
        urlParameters.add(new BasicNameValuePair("resource", "https://database.windows.net"));

        try {
            post.setEntity(new UrlEncodedFormEntity(urlParameters));
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        try (CloseableHttpClient httpClient = HttpClients.createDefault();
             CloseableHttpResponse response = httpClient.execute(post)){

            result = EntityUtils.toString(response.getEntity());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        
        ObjectNode node;
        try {
            node = new ObjectMapper().readValue(result, ObjectNode.class);
        
        
        System.out.println(result);
        
        if (node.has("access_token")) {
            result = node.get("access_token").toString();           
        }
        System.out.println(result);
        System.out.println(result.substring(1, result.length()-1));
        
        
        //updating the token
        this.token = result.substring(1, result.length()-1);
        
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
tokenManagerRunnable.fetchToken()带来null,因为tokenManagerRunnable类尚未执行

我们如何实现等待ScheduledExecutorService完成任务,以便每隔45分钟从tokenManagerRunnable.fetchToken()而不是null获取值并在数据源中设置新值


有什么想法吗?

你已经知道,这是一个同步问题。 同步线程的方法主要有两种:

  • 同步使用join
  • 异步使用回调
从重复任务的异步性质来看,我认为最好的选择是使用回调。 这样,无论何时检索新的令牌值,都可以设置它

我在下面更新了您的代码:

//导入[…]
公共类TokenManagerRunnable实现可运行{
专用最终SQLServerDataSource ds;
/**
*使用数据源触发令牌持久性的新构造函数
*关于检索。
*@param-ds
*/
公共TokenManagerRunnable(SQLServerDataSource ds){
这是1.ds=ds;
}
/**
*检索时在数据源上持久化标记的新方法。
*@param令牌
*/
私有void setToken(字符串令牌){
System.out.println(“获取令牌----”+令牌);
this.ds.setAccessToken(令牌);
}
@凌驾
公开募捐{
//检索[……]
试一试{
//解析[…]
//更新令牌
this.setToken(result.substring(1,result.length()-1));
}捕获(IOE异常){
e、 printStackTrace();
}
}
}
如您所见,运行程序不需要任何状态,因为它将直接将结果流式传输到数据源。 您只需在构建时向运行程序提供此数据源

SQLServerDataSource ds=newsqlserverdatasource();
TokenManagerRunnable TokenManagerRunnable=新的TokenManagerRunnable(ds);
//在此运行调用结束时同步运行1次
//将设置令牌
tokenManagerRunnable.run();
//从现在开始,每45分钟计划一次令牌刷新
ScheduledExecutorService sches=执行者。newScheduledThreadPool(1);
Schedule.scheduleWithFixedDelay(tokenManagerRunnable,0,45,时间单位:分钟);
//使用令牌[…]
编辑 正如您在评论中所述,您需要先执行
Runnable
,然后才能联系数据库。 您需要同步执行此操作,或者在单独的线程中添加下面的代码,具体取决于您打算对应用程序执行的操作。下面的问题是您的应用程序是否依赖于此初始化

事实上,您可以调用
run()
方法,而无需将其放入线程中,这样就可以同步运行令牌更新。
这意味着您需要调用
tokenManagerRunnable.run()

如果我没有弄错您的问题,您可以使用
CompletableFuture
。您在
CompletableFuture
中包装
token
,调度线程完成它。由于
CompletableFuture
是一个
Future
,其他线程可以等待结果

下面是一个基本的实现,演示了该机制

import java.util.concurrent.CompletableFuture;
班长{
静态CompletableFuture令牌=新的CompletableFuture();
公共静态void main(字符串[]args){
新线程(()->{
对于(int i=0;i<100_000;i++){
Math.log(Math.sqrt(i));
如果(i%10\u 000\u 000==0){
System.out.println(“做事”);
}
}
标记。完成(“结果”);
}).start();
String result=token.join();//等待直到设置了token并获取它
System.out.println(“got”+结果);
}
}

请记住,在获得结果后,您必须分配一个新的
CompletableFuture
。这是因为它们只能完成一次

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.sqlserver.jdbc.SQLServerDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
 
import java.util.ArrayList;
import java.util.List;
 
public class AzureServicePrincipleTokenManager implements Runnable {
 
 
    SQLServerDataSource ds ;
    String secret;
    String clientId;
    String tenetId;
    static final String RESOURCE = "https://database.windows.net";
    static final String ENDPOINT = "https://login.microsoftonline.com/";
    static final String GRANT_TYPE = "client_credentials";
    boolean confirmTokenFlag=false;
    private static Log logger = LogFactory.getLog( "AzureServicePrincipleTokenManager" );
 
    public AzureServicePrincipleTokenManager(SQLServerDataSource ds, String tenetId, String clientId, String secret) {
        this.ds = ds;
        this.secret = secret;
        this.clientId = clientId;
        this.tenetId = tenetId;
    }
 
    public boolean getConfirmTokenFlag(){
        return this.confirmTokenFlag;
    }
 
    private void setAccessToken(String token){
        this.ds.setAccessToken(token);
    }
 
 
 
 
    @Override
    public void run() {
        logger.info("Fetching Service Principle accessToken");
        String result = "";
        HttpPost post = new HttpPost(ENDPOINT+this.tenetId+"/oauth2/token");
        List<NameValuePair> urlParameters = new ArrayList<>();
        urlParameters.add(new BasicNameValuePair("grant_type", GRANT_TYPE));
        urlParameters.add(new BasicNameValuePair("client_id", this.clientId));
        urlParameters.add(new BasicNameValuePair("client_secret", this.secret));
        urlParameters.add(new BasicNameValuePair("resource", RESOURCE));
 
        try{
 
            post.setEntity(new UrlEncodedFormEntity(urlParameters));
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = httpClient.execute(post);
            result = EntityUtils.toString(response.getEntity());
            ObjectNode node = new ObjectMapper().readValue(result, ObjectNode.class);
 
            if (node.has("access_token")) {
                result = node.get("access_token").toString();
            }
 
        }catch (Exception ex){
            logger.error(ex.getMessage(),ex);
        }
 
        this.setAccessToken(result.substring(1, result.length()-1));
        confirmTokenFlag=true;
        logger.info("set confirmTokenFlag true");
 
 
    }
} 

即使任务没有完成,我的调用线程仍在继续,我也使用了您的代码更改。我已经更新了这个问题,这样你就可以清楚地了解我的问题所在。因此,你需要第一次同步执行,我相应地用编辑部分更新了答案:)如果你已经解决了自己的问题,那么你可以将你的答案标记为解决方案。那么帖子就不会永远处于找不到解决方案的状态。
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.sqlserver.jdbc.SQLServerDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
 
import java.util.ArrayList;
import java.util.List;
 
public class AzureServicePrincipleTokenManager implements Runnable {
 
 
    SQLServerDataSource ds ;
    String secret;
    String clientId;
    String tenetId;
    static final String RESOURCE = "https://database.windows.net";
    static final String ENDPOINT = "https://login.microsoftonline.com/";
    static final String GRANT_TYPE = "client_credentials";
    boolean confirmTokenFlag=false;
    private static Log logger = LogFactory.getLog( "AzureServicePrincipleTokenManager" );
 
    public AzureServicePrincipleTokenManager(SQLServerDataSource ds, String tenetId, String clientId, String secret) {
        this.ds = ds;
        this.secret = secret;
        this.clientId = clientId;
        this.tenetId = tenetId;
    }
 
    public boolean getConfirmTokenFlag(){
        return this.confirmTokenFlag;
    }
 
    private void setAccessToken(String token){
        this.ds.setAccessToken(token);
    }
 
 
 
 
    @Override
    public void run() {
        logger.info("Fetching Service Principle accessToken");
        String result = "";
        HttpPost post = new HttpPost(ENDPOINT+this.tenetId+"/oauth2/token");
        List<NameValuePair> urlParameters = new ArrayList<>();
        urlParameters.add(new BasicNameValuePair("grant_type", GRANT_TYPE));
        urlParameters.add(new BasicNameValuePair("client_id", this.clientId));
        urlParameters.add(new BasicNameValuePair("client_secret", this.secret));
        urlParameters.add(new BasicNameValuePair("resource", RESOURCE));
 
        try{
 
            post.setEntity(new UrlEncodedFormEntity(urlParameters));
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = httpClient.execute(post);
            result = EntityUtils.toString(response.getEntity());
            ObjectNode node = new ObjectMapper().readValue(result, ObjectNode.class);
 
            if (node.has("access_token")) {
                result = node.get("access_token").toString();
            }
 
        }catch (Exception ex){
            logger.error(ex.getMessage(),ex);
        }
 
        this.setAccessToken(result.substring(1, result.length()-1));
        confirmTokenFlag=true;
        logger.info("set confirmTokenFlag true");
 
 
    }
} 
        SQLServerDataSource ds = new SQLServerDataSource();

        AzureServicePrincipleTokenManager azureServicePrincipleTokenManager = new AzureServicePrincipleTokenManager(ds,"your tenentID","your clientID","your secret");
        ScheduledExecutorService sches = Executors.newScheduledThreadPool(1);
        sches.scheduleWithFixedDelay(azureServicePrincipleTokenManager, 0, 45, TimeUnit.MINUTES);
        logger.info("----ExecuterService started the Runnable task");

        while (azureServicePrincipleTokenManager.getConfirmTokenFlag()!=true){

            ds.getAccessToken(); //I wonder If i leave while body balnk it never become true. so intentionally i'm calling ds.getAccessToken();
        }
            logger.info("----get the token after settingup  "+ds.getAccessToken());