


Problèmes de concurrence multithread Java causés par l'obtention du jeton d'accès WeChat
Feb 28, 2017 am 09:44 AMContexte?:
access_token est le ticket globalement unique du compte officiel. L'access_token est requis lorsque le compte officiel appelle chaque interface. Les développeurs doivent le stocker correctement. Au moins 512 caractères d'espace doivent être réservés pour le stockage access_token. La période de validité de access_token est actuellement de 2 heures et doit être actualisée régulièrement. Une acquisition répétée rendra le dernier access_token invalide.
1、為了保密appsecrect,第三方需要一個(gè)access_token獲取和刷新的中控服務(wù)器。而其他業(yè)務(wù)邏輯服務(wù)器所使用的access_token均來自于該中控服務(wù)器,不應(yīng)該各自去刷新,否則會造成access_token覆蓋而影響業(yè)務(wù); 2、目前access_token的有效期通過返回的expire_in來傳達(dá),目前是7200秒之內(nèi)的值。中控服務(wù)器需要根據(jù)這個(gè)有效時(shí)間提前去刷新新access_token。在刷新過程中,中控服務(wù)器對外輸出的依然是老access_token,此時(shí)公眾平臺后臺會保證在刷新短時(shí)間內(nèi),新老access_token都可用,這保證了第三方業(yè)務(wù)的平滑過渡; 3、access_token的有效時(shí)間可能會在未來有調(diào)整,所以中控服務(wù)器不僅需要內(nèi)部定時(shí)主動刷新,還需要提供被動刷新access_token的接口,這樣便于業(yè)務(wù)服務(wù)器在API調(diào)用獲知access_token已超時(shí)的情況下,可以觸發(fā)access_token的刷新流程。 簡單起見,使用一個(gè)隨servlet容器一起啟動的servlet來實(shí)現(xiàn)獲取access_token的功能,具體為:因?yàn)樵搒ervlet隨著web容器而啟動,在該servlet的init方法中觸發(fā)一個(gè)線程來獲得access_token,該線程是一個(gè)無線循環(huán)的線程,每隔2個(gè)小時(shí)刷新一次access_token。相關(guān)代碼如下: :
public class InitServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void init(ServletConfig config) throws ServletException { new Thread(new AccessTokenThread()).start(); } }
2) Code du fil ?:
public class AccessTokenThread implements Runnable { public static AccessToken accessToken; @Override public void run() { while(true) { try{ AccessToken token = AccessTokenUtil.freshAccessToken(); // 從微信服務(wù)器刷新access_token if(token != null){ accessToken = token; }else{ System.out.println("get access_token failed------------------------------"); } }catch(IOException e){ e.printStackTrace(); } try{ if(null != accessToken){ Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒 }else{ Thread.sleep(60 * 1000); // 如果access_token為null,60秒后再獲取 } }catch(InterruptedException e){ try{ Thread.sleep(60 * 1000); }catch(InterruptedException e1){ e1.printStackTrace(); } } } } }
3) Code AccessToken ?:
public class AccessToken { private String access_token; private long expire_in; // access_token有效時(shí)間,單位為妙 public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this.access_token = access_token; } public long getExpire_in() { return expire_in; } public void setExpire_in(long expire_in) { this.expire_in = expire_in; } }
4) servlet dans la configuration web.xml
<servlet> <servlet-name>initServlet</servlet-name> <servlet-class>com.sinaapp.wx.servlet.InitServlet</servlet-class> <load-on-startup>0</load-on-startup> </servlet>
étant donné que initServlet définit load-on-startup=0, il est garanti de démarrer avant toutes les autres servlets.
Les autres servlets qui souhaitent utiliser access_token n'ont qu'à appeler AccessTokenThread.accessToken.
Entra?ne des problèmes de concurrence multithread ?:
1) Il ne semble y avoir aucun problème avec la mise en ?uvre ci-dessus, mais si vous y réfléchissez soigneusement, le accessToken dans la classe AccessTokenThread a le problème de l'accès simultané. Il n'est mis à jour que toutes les 2 heures par AccessTokenThread, mais il y aura de nombreux threads pour le lire. C'est un scénario typique de plus de lecture et moins d'écriture. et un seul thread écrit. Puisqu'il y a des lectures et des écritures simultanées, il doit y avoir un problème avec le code ci-dessus.
La fa?on la plus simple de penser est d'utiliser synchronisé?:
public class AccessTokenThread implements Runnable { private static AccessToken accessToken; @Override public void run() { while(true) { try{ AccessToken token = AccessTokenUtil.freshAccessToken(); // 從微信服務(wù)器刷新access_token if(token != null){ AccessTokenThread.setAccessToken(token); }else{ System.out.println("get access_token failed"); } }catch(IOException e){ e.printStackTrace(); } try{ if(null != accessToken){ Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒 }else{ Thread.sleep(60 * 1000); // 如果access_token為null,60秒后再獲取 } }catch(InterruptedException e){ try{ Thread.sleep(60 * 1000); }catch(InterruptedException e1){ e1.printStackTrace(); } } } } public synchronized static AccessToken getAccessToken() { return accessToken; } private synchronized static void setAccessToken(AccessToken accessToken) { AccessTokenThread2.accessToken = accessToken; } }
accessToken devient privé, et setAccessToken devient également remplacé privé et ajouté une méthode synchronisée pour accéder à accessToken.
Alors c'est parfait maintenant ? Il n'y a pas de problème ? En y réfléchissant bien, il y a toujours un problème. Le problème réside dans la définition de la classe AccessToken. Elle fournit ensuite une méthode d'ensemble publique. Ensuite, tous les threads peuvent utiliser AccessTokenThread.getAccessToken() pour obtenir le accessToken partagé par tous les threads. Modifiez ses propriétés ! ! ! ! Et c’est définitivement faux et cela ne devrait pas être fait.
2) Solution 1?:
On laisse la méthode AccessTokenThread.getAccessToken() renvoyer une copie de l'objet accessToken, afin que les autres Le thread ne peut pas modifier le accessToken dans la classe AccessTokenThread. Modifiez simplement la méthode AccessTokenThread.getAccessToken() comme suit?:
public synchronized static AccessToken getAccessToken() { AccessToken at = new AccessToken(); at.setAccess_token(accessToken.getAccess_token()); at.setExpire_in(accessToken.getExpire_in()); return at; }
Vous pouvez également implémenter la méthode clone dans la classe AccessToken. Les principes sont les mêmes. . Bien entendu, setAccessToken est également devenu privé.
3) Solution deux ?:
Puisque nous ne devrions pas laisser l'objet AccessToken être modifié, alors pourquoi ne définissons-nous pas accessToken comme un Des ? objets immuables ? ? Les modifications pertinentes sont les suivantes?:
public class AccessToken { private final String access_token; private final long expire_in; // access_token有效時(shí)間,單位為妙 public AccessToken(String access_token, long expire_in) { this.access_token = access_token; this.expire_in = expire_in; } public String getAccess_token() { return access_token; } public long getExpire_in() { return expire_in; } }
Comme indiqué ci-dessus, toutes les propriétés d'AccessToken sont définies comme types finaux, et seuls les constructeurs et les méthodes get sont fournis. . Dans ce cas, les autres threads ne peuvent pas le modifier après avoir obtenu l'objet AccessToken. La modification nécessite que l'objet AccessToken renvoyé dans AccessTokenUtil.freshAccessToken() ne puisse être créé que via le constructeur avec des paramètres. Dans le même temps, setAccessToken de AccessTokenThread doit également être modifié en privé et getAccessToken n'a pas besoin de renvoyer une copie.
Notez que les objets immuables doivent remplir les trois conditions suivantes :
a) L'état de l'objet ne peut pas être modifié après sa création
b) Tous les champs du ; les objets sont de type final
c) L'objet est créé correctement (c'est-à-dire que dans le constructeur de l'objet, cette référence ne s'échappe pas
4) Solution 3:
Existe-t-il une autre méthode meilleure, plus parfaite et plus efficace ? Analysons-le. Dans la deuxième solution, AccessTokenUtil.freshAccessToken() renvoie un objet immuable, puis appelle la méthode privée AccessTokenThread.setAccessToken(AccessToken accessToken) pour attribuer la valeur. Quel r?le joue la synchronisation synchronisée dans cette méthode ? Parce que l'objet est immuable et qu'un seul thread peut appeler la méthode setAccessToken, synchronisé ici ne joue pas de r?le "d'exclusion mutuelle" (car un seul thread peut le modifier), mais joue uniquement un r?le en assurant la "visibilité". visible pour les autres threads, c'est-à-dire permettre aux autres threads d'accéder au dernier objet accessToken. Pour assurer la "visibilité", volatile peut être utilisé, donc la synchronisation ici ne devrait pas être nécessaire. Nous utilisons volatile pour le remplacer. Le code modifié pertinent est le suivant :
public class AccessTokenThread implements Runnable { private static volatile AccessToken accessToken; @Override public void run() { while(true) { try{ AccessToken token = AccessTokenUtil.freshAccessToken(); // 從微信服務(wù)器刷新access_token if(token != null){ AccessTokenThread2.setAccessToken(token); }else{ System.out.println("get access_token failed"); } }catch(IOException e){ e.printStackTrace(); } try{ if(null != accessToken){ Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒 }else{ Thread.sleep(60 * 1000); // 如果access_token為null,60秒后再獲取 } }catch(InterruptedException e){ try{ Thread.sleep(60 * 1000); }catch(InterruptedException e1){ e1.printStackTrace(); } } } } private static void setAccessToken(AccessToken accessToken) { AccessTokenThread2.accessToken = accessToken; } public static AccessToken getAccessToken() { return accessToken; } }
Vous pouvez également le modifier comme ceci :
public class AccessTokenThread implements Runnable { private static volatile AccessToken accessToken; @Override public void run() { while(true) { try{ AccessToken token = AccessTokenUtil.freshAccessToken(); // 從微信服務(wù)器刷新access_token if(token != null){ accessToken = token; }else{ System.out.println("get access_token failed"); } }catch(IOException e){ e.printStackTrace(); } try{ if(null != accessToken){ Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒 }else{ Thread.sleep(60 * 1000); // 如果access_token為null,60秒后再獲取 } }catch(InterruptedException e){ try{ Thread.sleep(60 * 1000); }catch(InterruptedException e1){ e1.printStackTrace(); } } } } public static AccessToken getAccessToken() { return accessToken; } }
Vous pouvez également le modifier comme ceci :
public class AccessTokenThread implements Runnable { public static volatile AccessToken accessToken; @Override public void run() { while(true) { try{ AccessToken token = AccessTokenUtil.freshAccessToken(); // 從微信服務(wù)器刷新access_token if(token != null){ accessToken = token; }else{ System.out.println("get access_token failed"); } }catch(IOException e){ e.printStackTrace(); } try{ if(null != accessToken){ Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒 }else{ Thread.sleep(60 * 1000); // 如果access_token為null,60秒后再獲取 } }catch(InterruptedException e){ try{ Thread.sleep(60 * 1000); }catch(InterruptedException e1){ e1.printStackTrace(); } } } } }
accesToken變成了public,可以直接是一個(gè)AccessTokenThread.accessToken來訪問。但是為了后期維護(hù),最好還是不要改成public.
其實(shí)這個(gè)問題的關(guān)鍵是:在多線程并發(fā)訪問的環(huán)境中如何正確的發(fā)布一個(gè)共享對象。
其實(shí)我們也可以使用Executors.newScheduledThreadPool來搞定:
public class InitServlet2 extends HttpServlet { private static final long serialVersionUID = 1L; public void init(ServletConfig config) throws ServletException { ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); executor.scheduleAtFixedRate(new AccessTokenRunnable(), 0, 7200-200, TimeUnit.SECONDS); } }
public class AccessTokenRunnable implements Runnable { private static volatile AccessToken accessToken; @Override public void run() { try{ AccessToken token = AccessTokenUtil.freshAccessToken(); // 從微信服務(wù)器刷新access_token if(token != null){ accessToken = token; }else{ System.out.println("get access_token failed"); } }catch(IOException e){ e.printStackTrace(); } } public static AccessToken getAccessToken() { while(accessToken == null){ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } return accessToken; } }
獲取accessToken方式變成了:AccessTokenRunnable.getAccessToken();
?更多由獲取微信access_token引出的Java多線程并發(fā)問題相關(guān)文章請關(guān)注PHP中文網(wǎng)!

Outils d'IA chauds

Undress AI Tool
Images de déshabillage gratuites

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
échangez les visages dans n'importe quelle vidéo sans effort grace à notre outil d'échange de visage AI entièrement gratuit?!

Article chaud

Outils chauds

Bloc-notes++7.3.1
éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

PHP est un langage de script open source largement utilisé dans le développement Web et la programmation c?té serveur, notamment dans le développement de WeChat. Aujourd'hui, de plus en plus d'entreprises et de développeurs commencent à utiliser PHP pour le développement de WeChat, car il est devenu un langage de développement vraiment facile à apprendre et à utiliser. Dans le développement de WeChat, le cryptage et le décryptage des messages sont une question très importante car ils impliquent la sécurité des données. Pour les messages sans méthodes de cryptage et de décryptage, les pirates peuvent facilement obtenir les données, ce qui constitue une menace pour les utilisateurs.

Dans le développement des comptes publics WeChat, la fonction de vote est souvent utilisée. La fonction de vote est un excellent moyen pour les utilisateurs de participer rapidement aux interactions, et c'est également un outil important pour organiser des événements et recueillir des opinions. Cet article vous présentera comment utiliser PHP pour implémenter la fonction de vote WeChat. Obtenez l'autorisation du compte officiel WeChat. Tout d'abord, vous devez obtenir l'autorisation du compte officiel WeChat. Sur la plateforme publique WeChat, vous devez configurer l'adresse API du compte public WeChat, le compte officiel et le token correspondant au compte public. Dans le processus de développement en langage PHP, nous devons utiliser le PH officiellement fourni par WeChat

Avec la popularité de WeChat, de plus en plus d’entreprises commencent à l’utiliser comme outil marketing. La fonction de messagerie de groupe WeChat est l'un des moyens importants permettant aux entreprises de mener du marketing WeChat. Cependant, si vous comptez uniquement sur l’envoi manuel, il s’agit d’une tache extrêmement longue et laborieuse pour les spécialistes du marketing. Il est donc particulièrement important de développer un outil de messagerie de masse WeChat. Cet article présentera comment utiliser PHP pour développer des outils d'envoi de masse WeChat. 1. Travail de préparation Pour développer les outils d'envoi de masse WeChat, nous devons ma?triser les points techniques suivants : Connaissance de base du développement de la plateforme publique PHP WeChat Outils de développement : Sub

WeChat est actuellement l'une des plateformes sociales comptant la plus grande base d'utilisateurs au monde. Avec la popularité de l'Internet mobile, de plus en plus d'entreprises commencent à prendre conscience de l'importance du marketing WeChat. Lors du marketing WeChat, le service client est un élément crucial. Afin de mieux gérer la fenêtre de discussion du service client, nous pouvons utiliser le langage PHP pour le développement de WeChat. 1. Introduction au développement PHP WeChat PHP est un langage de script open source c?té serveur largement utilisé dans le domaine du développement Web. En combinaison avec l'interface de développement fournie par la plateforme publique WeChat, nous pouvons utiliser le langage PHP pour mener WeChat

Dans le développement des comptes publics WeChat, la gestion des balises utilisateur est une fonction très importante, qui permet aux développeurs de mieux comprendre et gérer leurs utilisateurs. Cet article explique comment utiliser PHP pour implémenter la fonction de gestion des balises utilisateur WeChat. 1. Obtenez l'openid de l'utilisateur WeChat Avant d'utiliser la fonction de gestion des balises utilisateur WeChat, nous devons d'abord obtenir l'openid de l'utilisateur. Dans le développement des comptes publics WeChat, il est courant d'obtenir l'openid via l'autorisation de l'utilisateur. Une fois l'autorisation de l'utilisateur terminée, nous pouvons obtenir l'utilisateur via le code suivant

Alors que WeChat devient un outil de communication de plus en plus important dans la vie des gens, sa fonction de messagerie agile est rapidement privilégiée par un grand nombre d'entreprises et de particuliers. Pour les entreprises, développer WeChat en une plate-forme marketing est devenu une tendance, et l'importance du développement de WeChat est progressivement devenue plus importante. Parmi eux, la fonction d'envoi de groupe est encore plus largement utilisée. Alors, en tant que programmeur PHP, comment implémenter des enregistrements d'envoi de messages de groupe ? Ce qui suit vous donnera une brève introduction. 1. Comprendre les connaissances en développement liées aux comptes publics WeChat. Avant de comprendre comment mettre en ?uvre des enregistrements d'envoi de messages de groupe, je

Comment utiliser PHP pour développer des comptes publics WeChat Les comptes publics WeChat sont devenus un canal important de promotion et d'interaction pour de nombreuses entreprises, et PHP, en tant que langage Web couramment utilisé, peut également être utilisé pour développer des comptes publics WeChat. Cet article présentera les étapes spécifiques pour utiliser PHP pour développer des comptes publics WeChat. étape 1 : Obtenez le compte développeur du compte officiel WeChat. Avant de commencer le développement du compte officiel WeChat, vous devez demander un compte développeur du compte officiel WeChat. Pour le processus d'inscription spécifique, veuillez vous référer au site officiel de la plateforme publique WeChat

Avec le développement d’Internet et des appareils mobiles intelligents, WeChat est devenu un élément indispensable dans les domaines social et marketing. Dans cette ère de plus en plus numérique, la manière d'utiliser PHP pour le développement de WeChat est devenue la préoccupation de nombreux développeurs. Cet article présente principalement les points de connaissances pertinents sur la fa?on d'utiliser PHP pour le développement WeChat, ainsi que certains conseils et précautions. 1. Préparation de l'environnement de développement Avant de développer WeChat, vous devez d'abord préparer l'environnement de développement correspondant. Plus précisément, vous devez installer l'environnement d'exploitation PHP et la plateforme publique WeChat
