1.1、同步
同步请求是指在请求发起到拿到响应结果之前,程序一直会处于阻塞状态,无法接收新的请求,直至拿到响应
1.2、异步
异步请求是指请求发起后到拿到响应结果之前,程序是非阻塞状态,可以继续接收新的请求,响应回来时会调用一个回调处理响应数据
1.3、阻塞
阻塞是指请求结果返回之前,当前线程会被挂起。阻塞和同步实质上是不可等同的。对于同步来说,很多时候当前线程仍是处于激活状态的,可以继续处理其他各种消息,只是请求拿不到返回而已。
1.4、非阻塞
非阻塞与阻塞相对,在不能立刻得到结果之前,不会阻塞当前线程,会立刻返回
最后,阻塞和非阻塞对象不是一个绝对的概念,阻塞里也可能会融入非阻塞的处理;非阻塞里也可能融入阻塞的处理
2.1 优缺点
同步请求的好处在于:理解简单;实现简单;可以将响应结果返回做后续处理;
同步请求的缺点在于:不能支持高并发的情况,在对请求响应实时性要求不高的场景下,会影响性能
2.2 实现方式
public static String postForPBSerializeForm(String url, JSONObject requestParam){
System.out.println("requestParam: " + requestParam.toJSONString());
try {
OkHttpClient client = new OkHttpClient();
FormBody.Builder formBodyBuilder = new FormBody.Builder();
formBodyBuilder.add("id", requestParam.getString("id"));
formBodyBuilder.add("version", requestParam.getString("version"));
formBodyBuilder.add("mockData", requestParam.getString("mockData"));
formBodyBuilder.add("type", requestParam.getString("type"));
Request request = new Request.Builder()
.post(formBodyBuilder.build())
.url(url)
.build();
//okhttp 同步请求关键代码
Response response = client.newCall(request).execute();
System.out.println("response.code():" + response.code() + " " + (response.code() == 200));
if (response.code() == 200) {
return response.body().string();
}
}catch (Exception e){
System.out.println("'sdew");
e.printStackTrace();
}
return null;
}
3.1 优缺点
异步请求的好处在于:支持高并发的接口请求
异步请求的缺点在于:实现复杂;响应结果只能输出,不能返回,不利于接口的后续处理
3.2 实现方式
public static String postForPBSerializeFormAsync(String url, JSONObject requestParam){
System.out.println("requestParam: " + requestParam.toJSONString());
try {
OkHttpClient client = new OkHttpClient();
FormBody.Builder formBodyBuilder = new FormBody.Builder();
formBodyBuilder.add("id", requestParam.getString("id"));
formBodyBuilder.add("version", requestParam.getString("version"));
formBodyBuilder.add("mockData", requestParam.getString("mockData"));
formBodyBuilder.add("type", requestParam.getString("type"));
Request request = new Request.Builder()
.post(formBodyBuilder.build())
.url(url)
.build();
//okhttp异步请求的关键代码
Call call=client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("shibai");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("success:" + response.body().string());
}
});
}catch (Exception e){
System.out.println("'sdew");
e.printStackTrace();
}
return null;
}
3.3 具体执行流程图
3.4 相关知识点介绍
3.4.1 Call
Call是一个接口, A call is a request that has been prepared for execution. A call can be canceled. As this object represents a single request/response pair (stream), it cannot be executed twice.是源码中给出的它的作用和特点。即:(1)一个被准备执行的请求;(2)可以被取消;(3)一个call只能负责去执行一个请求
由于 OkHttpClient 实现了 Call.Factory,因此它具备创建 Call 对象的功能;
OkHttpClient部分源码:
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
}
3.4.2 RealCall(Call 的实现类)
RealCall真正实现了Call接口类内部定义的同步与异步请求、取消请求等方法
RealCall的关键属性
3.4.3 Dispatcher(异步任务分发器)
异步请求时才会用到Dispatcher,它会内部指定线程池去执行异步任务,并在执行完毕之后提供了finish方法结束异步请求。然后从等待队列中获取下一个满足条件的异步任务去执行。
源码:
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import okhttp3.RealCall.AsyncCall;
import okhttp3.internal.Util;
/**
* Policy on when async requests are executed.
* * <p>Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
* own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
* of calls concurrently.
*/
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven' t finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
/**
* Set the maximum number of requests to execute concurrently. Above this requests queue in
* memory, waiting for the running calls to complete.
* * If more than {@code maxRequests} requests are in flight when this is invoked, those requests
* will remain in flight.
*/
public void setMaxRequests(int maxRequests) {
if (maxRequests < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequests);
}
synchronized (this) {
this.maxRequests = maxRequests;
}
promoteAndExecute();
}
public synchronized int getMaxRequests() {
return maxRequests;
}
/**
* Set the maximum number of requests for each host to execute concurrently. This limits requests
* by the URL'
s host name. Note that concurrent requests to a single IP address may still exceed
* this limit: multiple hostnames may share an IP address or be routed through the same HTTP
* proxy.
* * <p>If more than {@code maxRequestsPerHost} requests are in flight when this is invoked, those
* requests will remain in flight.
* * <p>WebSocket connections to hosts <b>do not</b> count against this limit.
*/
public void setMaxRequestsPerHost(int maxRequestsPerHost) {
if (maxRequestsPerHost < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
}
synchronized (this) {
this.maxRequestsPerHost = maxRequestsPerHost;
}
promoteAndExecute();
}
public synchronized int getMaxRequestsPerHost() {
return maxRequestsPerHost;
}
/**
* Set a callback to be invoked each time the dispatcher becomes idle (when the number of running
* calls returns to zero).
* * <p>Note: The time at which a {@linkplain Call call} is considered idle is different depending
* on whether it was run {@linkplain Call#enqueue(Callback) asynchronously} or
* {@linkplain Call#execute() synchronously}. Asynchronous calls become idle after the
* {@link Callback#onResponse onResponse} or {@link Callback#onFailure onFailure} callback has
* returned. Synchronous calls become idle once {@link Call#execute() execute()} returns. This
* means that if you are doing synchronous calls the network layer will not truly be idle until
* every returned {@link Response} has been closed.
*/
public synchronized void setIdleCallback(@Nullable Runnable idleCallback) {
this.idleCallback = idleCallback;
}
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
@Nullable private AsyncCall findExistingCallWithHost(String host) {
for (AsyncCall existingCall : runningAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
for (AsyncCall existingCall : readyAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
return null;
}
/**
* Cancel all calls currently enqueued or executing. Includes calls executed both {@linkplain
* Call#execute() synchronously} and {@linkplain Call#enqueue asynchronously}.
*/
public synchronized void cancelAll() {
for (AsyncCall call : readyAsyncCalls) {
call.get().cancel();
}
for (AsyncCall call : runningAsyncCalls) {
call.get().cancel();
}
for (RealCall call : runningSyncCalls) {
call.cancel();
}
}
/**
* Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
* them on the executor service. Must not be called with synchronization because executing calls
* can call into user code.
* * @return true if the dispatcher is currently running calls.
*/
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
call.callsPerHost().decrementAndGet();
finished(runningAsyncCalls, call);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
/** Returns a snapshot of the calls currently awaiting execution. */
public synchronized List<Call> queuedCalls() {
List<Call> result = new ArrayList<>();
for (AsyncCall asyncCall : readyAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
/** Returns a snapshot of the calls currently being executed. */
public synchronized List<Call> runningCalls() {
List<Call> result = new ArrayList<>();
result.addAll(runningSyncCalls);
for (AsyncCall asyncCall : runningAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
public synchronized int queuedCallsCount() {
return readyAsyncCalls.size();
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
}
Dispatcher 的关键属性,这些属性会影响异步请求的执行
3.4.4 AsyncCall
AsyncCall类是定义在RealCall类中的内部类,表示一个异步请求。在 Dispatcher 中分发的异步请求任务就是 AsyncCall,Dispatcher会从线程池中指定某个线程去执行AsyncCall任务。在 Dispatcher 内部定义了两个队列来存储 AsyncCall,分别是 readyAsyncCalls 和
runningAsyncCalls。它们分别表示准备要执行的 AsyncCall 队列和正在执行的 AsycnCall 队列;当然还有一个 runningSyncCalls 这个队列,但是它适用于存放 RealCall ,也就是用于存储同步请求的任务。
参考博客:
Okhttp之同步和异步请求简单分析
OKHTTP异步和同步请求简单分析