획득 위 챗 accesstoken 이 끌 어 낸 자바 다 중 스 레 드 병행 문제
17005 단어 자바 다 중 스 레 드
access_token 은 공중전화 의 전체 국면 에서 유일한 어음 으로 공중전화 가 각 인 터 페 이 스 를 호출 할 때 access 를 사용 해 야 합 니 다.token。개발 자 는 잘 저장 해 야 합 니 다.access_token 의 저장 소 는 최소 512 개의 문자 공간 을 유지 해 야 합 니 다.access_token 의 유효기간 은 현재 2 시간 입 니 다.정시 에 갱신 해 야 합 니 다.중복 획득 은 지난번 에 얻 은 access 를 가 져 옵 니 다.token 실효.
1、 appsecrect, access_token 。 access_token , , access_token ;
2、 access_token expire_in , 7200 。 access_token。 , access_token, , access_token , ;
3、access_token , , access_token , API access_token , access_token 。
, servlet servlet access_token , : servlet web , servlet init access_token, , 2 access_token。 :
1)servlet :
public class InitServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
public void init(ServletConfig config) throws ServletException
{
new Thread(new AccessTokenThread()).start();
}
}
2)스 레 드 코드:
public class AccessTokenThread implements Runnable
{
public static AccessToken accessToken;
@Override
public void run()
{
while(true)
{
try{
AccessToken token = AccessTokenUtil.freshAccessToken(); // 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 ,
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 웹 xml 에서 의 설정
<servlet>
<servlet-name>initServlet</servlet-name>
<servlet-class>com.sinaapp.wx.servlet.InitServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
initServlet 에 load-on-startup=0 이 설정 되 어 있 기 때문에 모든 servlet 전에 시작 할 것 을 보증 합 니 다.
다른 servlet 는 access 를 사용 해 야 합 니 다.token 은 AccessTokenThread.accessToken 만 호출 하면 됩 니 다.
다 중 스 레 드 병발 문제:
1)위의 실현 은 아무런 문제 가 없 는 것 같 지만 자세히 생각해 보면 AccessTokenThread 류 의 accessToken 은 동시 방문 문제 가 존재 합 니 다.AccessTokenThread 는 2 시간 마다 한 번 씩 업데이트 되 지만 많은 스 레 드 로 읽 을 수 있 습 니 다.이것 은 전형 적 인 읽 기와 쓰기 가 적은 장면 이 고 하나의 스 레 드 만 있 습 니 다.동시 읽 기와 쓰기 가 존재 하 는 이상 위의 코드 는 틀림없이 문제 가 있 을 것 이다.
일반적으로 생각 하 는 가장 쉬 운 방법 은 synchronized 로 처리 하 는 것 입 니 다.
public class AccessTokenThread implements Runnable
{
private static AccessToken accessToken;
@Override
public void run()
{
while(true)
{
try{
AccessToken token = AccessTokenUtil.freshAccessToken(); // 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 가 accessToken 에 접근 하 는 방법 을 추 가 했 습 니 다.
그럼 여기까지 면 완벽 하지 않 을까요?문제 없 는데?자세히 생각해 보면 문제 가 있 습 니 다.문 제 는 AccessToken 류 의 정의 에 있어 서 Public set 방법 을 제공 합 니 다.그러면 모든 스 레 드 는 AccessTokenThread.getAccessToken()을 사용 하여 모든 스 레 드 가 공유 하 는 accessToken 을 얻 은 후에 모든 스 레 드 가 속성 을 수정 할 수 있 습 니 다!!!이것 은 틀림없이 옳지 않 고,해 서 는 안 된다.
2)해결 방법 1:
우 리 는 AccessTokenThread.getAccessToken()방법 으로 accessToken 대상 의 copy,사본 을 되 돌려 줍 니 다.그러면 다른 스 레 드 는 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 류 에서 clone 방법 을 실현 할 수도 있 고 원 리 는 모두 같다.물론 setAccessToken 도 private 가 되 었 다.
3)해결 방법 2:
우리 가 AccessToken 의 대상 을 수정 하지 말 아야 하 는 이상,우 리 는 왜 accessToken 을'불가 변 대상'으로 정의 하지 않 습 니까?관련 수정 사항 은 다음 과 같 습 니 다.
public class AccessToken
{
private final String access_token;
private final long expire_in; // access_token ,
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 형식 으로 정의 되 었 고 구조 함수 와 get 방법 만 제공 합 니 다.이렇게 되면 다른 스 레 드 는 AccessToken 의 대상 을 얻 은 후에 수정 할 수 없습니다.수정 요구 AccessTokenUtil.freshAccessToken()에서 되 돌아 오 는 AccessToken 의 대상 은 참 이 있 는 구조 함수 로 만 만 만 만 들 수 있 습 니 다.또한 AccessTokenThread 의 setAccessToken 도 private 로 수정 해 야 합 니 다.getAccessToken 은 복사 본 을 되 돌려 줄 필요 가 없습니다.
가 변 적 이지 않 은 대상 은 반드시 아래 의 세 가지 조건 을 만족 시 켜 야 한다.
a)대상 이 생 성 된 후 상 태 를 수정 할 수 없습니다.
b)대상 의 모든 도 메 인 은 final 형식 입 니 다.
c)대상 은 정확하게 만 들 어 졌 습 니 다(즉,대상 의 구조 함수 에서 this 인용 은 일출 이 발생 하지 않 았 습 니 다).
4)해결 방법 3:
더 좋 고,더 완벽 하고,더 효율 적 인 방법 은 없 을 까?해결 방법 2 에서 AccessTokenUtil.freshAccessToken()은 가 변 적 이지 않 은 대상 으로 돌아 간 다음 에 private 의 AccessTokenThread.setAccessToken(AccessToken accessToken)방법 으로 값 을 부여 합 니 다.이 방법의 synchronized 동기 화 는 어떤 작용 을 했 습 니까?대상 이 가 변 적 이지 않 고 하나의 스 레 드 만 setAccessToken 방법 을 호출 할 수 있 기 때문에 이곳 의 synchronized 는'상호 배척'역할 을 하지 않 습 니 다(하나의 스 레 드 만 수정 되 었 기 때 문 입 니 다).단지'가시 성'을 확보 하 는 역할 을 했 을 뿐 다른 스 레 드 에 대한 수정 을 볼 수 있 습 니 다.즉,다른 스 레 드 가 접근 하 는 것 은 모두 최신 accessToken 대상 입 니 다.그리고'가시 성'은 volatile 을 사용 하여 진행 할 수 있 기 때문에 이곳 의 synchronized 는 필요 하지 않 을 것 입 니 다.우 리 는 volatile 을 사용 하여 대체 할 것 입 니 다.관련 수정 코드 는 다음 과 같 습 니 다.
public class AccessTokenThread implements Runnable
{
private static volatile AccessToken accessToken;
@Override
public void run()
{
while(true)
{
try{
AccessToken token = AccessTokenUtil.freshAccessToken(); // 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(); // 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(); // 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 로 바 뀌 었 습 니 다.AccessTokenThread.accessToken 에서 직접 방문 할 수 있 습 니 다.그러나 후기 유 지 를 위해 서 는 대중 으로 바 꾸 지 않 는 것 이 좋다.
사실 이 문제 의 관건 은 다 중 스 레 드 동시 방문 환경 에서 공유 대상 을 어떻게 정확하게 발표 하 느 냐 하 는 것 이다.
사실 우 리 는 Executors.new Scheduled ThreadPool 을 사용 하여 해결 할 수 있 습 니 다.
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(); // 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()으로 바 뀌 었 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JAVA 다 중 스 레 드 다운로드 및 정지점 전송정지점 전송 과 다 중 스 레 드 다운로드 원 리 는 같 습 니 다. http2.setRequestProperty("RANGE","bytes="+startl+"-");//정지점 위 치 를 설정 하고 서버 에 파일 의...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.