国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

首頁(yè) 微信小程式 微信開(kāi)發(fā) 由取得微信access_token引出的Java多執(zhí)行緒並發(fā)問(wèn)題

由取得微信access_token引出的Java多執(zhí)行緒並發(fā)問(wèn)題

Feb 28, 2017 am 09:44 AM
微信開(kāi)發(fā)

背景

????? access_token是公有號(hào)的全域唯一票據(jù),公眾號(hào)呼叫各介面時(shí)需使用access_token。開(kāi)發(fā)者需要進(jìn)行妥善保存。 access_token的儲(chǔ)存至少要保留512個(gè)字元空間。 access_token的有效期限目前為2小時(shí),需定時(shí)刷新,重複取得將導(dǎo)致上次取得的access_token失效。

1、為了保密appsecrect,第三方需要一個(gè)access_token獲取和刷新的中控服務(wù)器。而其他業(yè)務(wù)邏輯服務(wù)器所使用的access_token均來(lái)自于該中控服務(wù)器,不應(yīng)該各自去刷新,否則會(huì)造成access_token覆蓋而影響業(yè)務(wù);
2、目前access_token的有效期通過(guò)返回的expire_in來(lái)傳達(dá),目前是7200秒之內(nèi)的值。中控服務(wù)器需要根據(jù)這個(gè)有效時(shí)間提前去刷新新access_token。在刷新過(guò)程中,中控服務(wù)器對(duì)外輸出的依然是老access_token,此時(shí)公眾平臺(tái)后臺(tái)會(huì)保證在刷新短時(shí)間內(nèi),新老access_token都可用,這保證了第三方業(yè)務(wù)的平滑過(guò)渡;
3、access_token的有效時(shí)間可能會(huì)在未來(lái)有調(diào)整,所以中控服務(wù)器不僅需要內(nèi)部定時(shí)主動(dòng)刷新,還需要提供被動(dòng)刷新access_token的接口,這樣便于業(yè)務(wù)服務(wù)器在API調(diào)用獲知access_token已超時(shí)的情況下,可以觸發(fā)access_token的刷新流程。

簡(jiǎn)單起見(jiàn),使用一個(gè)隨servlet容器一起啟動(dòng)的servlet來(lái)實(shí)現(xiàn)獲取access_token的功能,具體為:因?yàn)樵搒ervlet隨著web容器而啟動(dòng),在該servlet的init方法中觸發(fā)一個(gè)線程來(lái)獲得access_token,該線程是一個(gè)無(wú)線循環(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)執(zhí)行緒程式碼

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)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在web.xml中的設(shè)定

##?4)servlet在web.xml中的設(shè)定

#
  <servlet>
    <servlet-name>initServlet</servlet-name>
    <servlet-class>com.sinaapp.wx.servlet.InitServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>

因?yàn)閕nitServlet設(shè)定了load-on-startup=0,所以保證了在所有其它servlet之前啟動(dòng)。 其它servlet要使用access_token的只需要呼叫 AccessTokenThread.accessToken即可。

引出多執(zhí)行緒並發(fā)問(wèn)題

#1)上面的實(shí)作似乎沒(méi)有什麼問(wèn)題,但是仔細(xì)一想,AccessTokenThread類別中的accessToken ,它存在並發(fā)訪問(wèn)的問(wèn)題,它僅僅由AccessTokenThread每隔2小時(shí)更新一次,但是會(huì)有很多線程來(lái)讀取它,它是一個(gè)典型的讀多寫(xiě)少的場(chǎng)景,而且只有一個(gè)線程寫(xiě)。既然存在並發(fā)的讀寫(xiě),那麼上面的程式碼肯定是存在問(wèn)題的。

???? 一般想到的最簡(jiǎn)單的方法是使用synchronized來(lái)處理:

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變成了private,setAccessToken也變成了private ,增加了同步synchronized訪問(wèn)accessToken的方法。 那麼到這裡是不是就完美了呢?就沒(méi)有問(wèn)題了呢?仔細(xì)想想,還是有問(wèn)題,問(wèn)題出在AccessToken類別的定義上,它提供了public的set方法,那麼所有的線程都在使用AccessTokenThread.getAccessToken()獲得了所有線程共享的accessToken之後,任何線程都可以修改它的屬性! ! ! !而這肯定是不對(duì)的,不應(yīng)該的。

2)解決方法一

??? 我們讓AccessTokenThread.getAccessToken()方法傳回一個(gè)accessToken物件的copy,副本,這樣其它的執(zhí)行緒就無(wú)法修改AccessTokenThread類別中的accessToken了。以下修改AccessTokenThread.getAccessToken()方法即可:

	public synchronized static AccessToken getAccessToken() {
		AccessToken at = new AccessToken();
		at.setAccess_token(accessToken.getAccess_token());		
		at.setExpire_in(accessToken.getExpire_in());
		return at;
	}

?也可以在AccessToken類別中實(shí)作clone方法,原理都是一樣的。當(dāng)然setAccessToken也變成了private。

3)解決方法二

??? 既然我們不應(yīng)該讓AccessToken的物件被修改,那我們?yōu)槭颤N不將accessToken定義成一個(gè)“不可變物件”?相關(guān)修改如下:

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

?如上所示,AccessToken所有的屬性都定義成了final型別了,只提供建構(gòu)子和get方法。這樣的話,其他的執(zhí)行緒在獲得了AccessToken的物件之後,就無(wú)法修改了。改修改要求AccessTokenUtil.freshAccessToken()中傳回的AccessToken的物件只能透過(guò)有參的建構(gòu)子來(lái)建立。同時(shí)AccessTokenThread的setAccessToken也要修改成private,getAccessToken無(wú)須回傳一個(gè)副本了。

注意不可變物件必須滿足下面的三個(gè)條件:

a) 物件建立之後其狀態(tài)就不能修改;

b) 物件的所有域都是final型別;c) 物件是正確建立的(即在物件的建構(gòu)子中,this引用沒(méi)有發(fā)生逸出);

4)解決方法三

??? 還有沒(méi)有其他更好,更完美,更有效率的方法呢?讓我們分析一下,在解決方法二中,AccessTokenUtil.freshAccessToken()傳回的是一個(gè)不可變對(duì)象,然後呼叫private的AccessTokenThread.setAccessToken(AccessToken accessToken)方法來(lái)進(jìn)行賦值。這個(gè)方法上的synchronized同步起到了什麼作用呢?因?yàn)槲锛r(shí)不可變的,而且只有一個(gè)執(zhí)行緒可以呼叫setAccessToken方法,那麼這裡的synchronized沒(méi)有起到"互斥"的作用(因?yàn)橹挥幸粋€(gè)執(zhí)行緒修改),而僅僅是起到了保證「可見(jiàn)性」的作用,讓修改對(duì)其它的執(zhí)行緒可見(jiàn),也就是讓其他執(zhí)行緒存取到的都是最新的accessToken物件。而保證「可見(jiàn)性」是可以使用volatile來(lái)進(jìn)行的,所以這裡的synchronized應(yīng)該是沒(méi)有必要的,我們使用volatile來(lái)替代它。相關(guān)修改程式碼如下:

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

?也可以這樣改:

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

##?還可以這樣改:

###
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來(lái)訪問(wèn)。但是為了后期維護(hù),最好還是不要改成public.

其實(shí)這個(gè)問(wèn)題的關(guān)鍵是:在多線程并發(fā)訪問(wèn)的環(huán)境中如何正確的發(fā)布一個(gè)共享對(duì)象。

其實(shí)我們也可以使用Executors.newScheduledThreadPool來(lái)搞定:

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ā)問(wèn)題相關(guān)文章請(qǐng)關(guān)注PHP中文網(wǎng)!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願(yuàn)投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請(qǐng)聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開(kāi)發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺(jué)化網(wǎng)頁(yè)開(kāi)發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)程式碼編輯軟體(SublimeText3)

熱門(mén)話題

PHP微信開(kāi)發(fā):如何實(shí)作訊息加密解密 PHP微信開(kāi)發(fā):如何實(shí)作訊息加密解密 May 13, 2023 am 11:40 AM

PHP是一種開(kāi)源的腳本語(yǔ)言,廣泛應(yīng)用於網(wǎng)頁(yè)開(kāi)發(fā)和伺服器端編程,尤其在微信開(kāi)發(fā)中得到了廣泛的應(yīng)用。如今,越來(lái)越多的企業(yè)和開(kāi)發(fā)者開(kāi)始使用PHP進(jìn)行微信開(kāi)發(fā),因?yàn)樗蔀榱苏嬲囊讓W(xué)易用的開(kāi)發(fā)語(yǔ)言。在微信開(kāi)發(fā)中,訊息的加密和解密是一個(gè)非常重要的問(wèn)題,因?yàn)樗鼈兩婕百Y料的安全性。對(duì)於沒(méi)有加密和解密方式的消息,駭客可以輕鬆取得其中的數(shù)據(jù),對(duì)用戶造成威脅

PHP微信開(kāi)發(fā):如何實(shí)現(xiàn)投票功能 PHP微信開(kāi)發(fā):如何實(shí)現(xiàn)投票功能 May 14, 2023 am 11:21 AM

在微信公眾號(hào)開(kāi)發(fā)中,投票功能經(jīng)常被運(yùn)用。投票功能是讓使用者快速參與互動(dòng)的好方式,也是舉辦活動(dòng)和調(diào)查意見(jiàn)的重要工具。本文將為您介紹如何使用PHP實(shí)作微信投票功能。在取得微信公眾號(hào)授權(quán)首先,你需要取得微信公眾號(hào)的授權(quán)。在微信公眾平臺(tái)上,你需要設(shè)定微信公眾號(hào)碼的api地址、官方帳號(hào)和公眾號(hào)碼對(duì)應(yīng)的token。在我們使用PHP語(yǔ)言開(kāi)發(fā)的過(guò)程中,我們需要使用微信官方提供的PH

用PHP開(kāi)發(fā)微信群發(fā)工具 用PHP開(kāi)發(fā)微信群發(fā)工具 May 13, 2023 pm 05:00 PM

隨著微信的普及,越來(lái)越多的企業(yè)開(kāi)始將其作為行銷(xiāo)工具。而微信群發(fā)功能,則是企業(yè)進(jìn)行微信行銷(xiāo)的重要手段之一。但是,如果只依靠手動(dòng)發(fā)送,對(duì)於行銷(xiāo)人員來(lái)說(shuō)是一件極為費(fèi)時(shí)費(fèi)力的工作。所以,開(kāi)發(fā)一款微信群發(fā)工具就顯得格外重要。本文將介紹如何使用PHP開(kāi)發(fā)微信群發(fā)工具。一、準(zhǔn)備工作開(kāi)發(fā)微信群發(fā)工具,我們需要掌握以下幾個(gè)技術(shù)點(diǎn):PHP基礎(chǔ)知識(shí)微信公眾平臺(tái)開(kāi)發(fā)開(kāi)發(fā)工具:Sub

PHP微信開(kāi)發(fā):如何實(shí)現(xiàn)客服聊天視窗管理 PHP微信開(kāi)發(fā):如何實(shí)現(xiàn)客服聊天視窗管理 May 13, 2023 pm 05:51 PM

微信是目前全球用戶規(guī)模最大的社群平臺(tái)之一,隨著行動(dòng)網(wǎng)路的普及,越來(lái)越多的企業(yè)開(kāi)始意識(shí)到微信行銷(xiāo)的重要性。在進(jìn)行微信行銷(xiāo)時(shí),客服服務(wù)是至關(guān)重要的一環(huán)。為了更好地管理客服聊天窗口,我們可以藉助PHP語(yǔ)言進(jìn)行微信開(kāi)發(fā)。一、PHP微信開(kāi)發(fā)簡(jiǎn)介PHP是一種開(kāi)源的伺服器端腳本語(yǔ)言,廣泛用於Web開(kāi)發(fā)領(lǐng)域。結(jié)合微信公眾平臺(tái)提供的開(kāi)發(fā)接口,我們可以使用PHP語(yǔ)言進(jìn)行微信

PHP微信開(kāi)發(fā):如何實(shí)現(xiàn)使用者標(biāo)籤管理 PHP微信開(kāi)發(fā):如何實(shí)現(xiàn)使用者標(biāo)籤管理 May 13, 2023 pm 04:31 PM

在微信公眾號(hào)開(kāi)發(fā)中,使用者標(biāo)籤管理是一個(gè)非常重要的功能,可以讓開(kāi)發(fā)者更了解和管理自己的使用者。本篇文章將介紹如何使用PHP實(shí)作微信使用者標(biāo)籤管理功能。一、取得微信用戶openid在使用微信用戶標(biāo)籤管理功能之前,我們首先需要取得用戶的openid。在微信公眾號(hào)開(kāi)發(fā)中,透過(guò)使用者授權(quán)的方式取得openid是比較常見(jiàn)的做法。在使用者授權(quán)完成後,我們可以透過(guò)以下程式碼取得用

PHP微信開(kāi)發(fā):如何實(shí)作群發(fā)訊息傳送記錄 PHP微信開(kāi)發(fā):如何實(shí)作群發(fā)訊息傳送記錄 May 13, 2023 pm 04:31 PM

隨著微信成為了人們生活中越來(lái)越重要的通訊工具,其敏捷的訊息傳遞功能迅速受到廣大企業(yè)和個(gè)人的青睞。對(duì)企業(yè)而言,將微信發(fā)展為一個(gè)行銷(xiāo)平臺(tái)已經(jīng)成為趨勢(shì),而微信開(kāi)發(fā)的重要性也逐漸凸顯。在其中,群發(fā)功能更是被廣泛使用,那麼,作為PHP程式設(shè)計(jì)師,如何實(shí)現(xiàn)群發(fā)訊息發(fā)送記錄呢?以下將為大家簡(jiǎn)單介紹一下。 1.了解微信公眾號(hào)相關(guān)開(kāi)發(fā)知識(shí)在了解如何實(shí)現(xiàn)群發(fā)訊息發(fā)送記錄之前,我

使用PHP實(shí)現(xiàn)微信公眾號(hào)開(kāi)發(fā)的步驟 使用PHP實(shí)現(xiàn)微信公眾號(hào)開(kāi)發(fā)的步驟 Jun 27, 2023 pm 12:26 PM

如何使用PHP實(shí)現(xiàn)微信公眾號(hào)開(kāi)發(fā)微信公眾號(hào)已經(jīng)成為了許多企業(yè)推廣和互動(dòng)的重要管道,而PHP作為常用的Web語(yǔ)言,也可以用來(lái)進(jìn)行微信公眾號(hào)的開(kāi)發(fā)。本文將介紹使用PHP實(shí)現(xiàn)微信公眾號(hào)開(kāi)發(fā)的具體步驟。第一步:取得微信公眾號(hào)的開(kāi)發(fā)者帳號(hào)在開(kāi)始微信公眾號(hào)開(kāi)發(fā)之前,需要先去申請(qǐng)一個(gè)微信公眾號(hào)的開(kāi)發(fā)者帳號(hào)。具體的註冊(cè)流程可參考微信公眾平臺(tái)的官方網(wǎng)

如何使用PHP進(jìn)行微信開(kāi)發(fā)? 如何使用PHP進(jìn)行微信開(kāi)發(fā)? May 21, 2023 am 08:37 AM

隨著網(wǎng)路和行動(dòng)智慧型裝置的發(fā)展,微信成為了社交和行銷(xiāo)領(lǐng)域不可或缺的一部分。在這個(gè)越來(lái)越數(shù)位化的時(shí)代,如何使用PHP進(jìn)行微信開(kāi)發(fā)已經(jīng)成為了許多開(kāi)發(fā)者的關(guān)注點(diǎn)。本文主要介紹如何使用PHP進(jìn)行微信發(fā)展的相關(guān)知識(shí)點(diǎn),以及其中的一些技巧和注意事項(xiàng)。一、開(kāi)發(fā)環(huán)境準(zhǔn)備在進(jìn)行微信開(kāi)發(fā)之前,首先需要準(zhǔn)備好對(duì)應(yīng)的開(kāi)發(fā)環(huán)境。具體來(lái)說(shuō),需要安裝PHP的運(yùn)作環(huán)境,以及微信公眾平臺(tái)提

See all articles