1. 程式人生 > 實用技巧 >Java 非同步實現的幾種方式

Java 非同步實現的幾種方式

1. jdk1.8之前的Future

jdk併發包裡的Future代表了未來的某個結果,當我們向執行緒池中提交任務的時候會返回該物件,可以通過future獲得執行的結果,但是jdk1.8之前的Future有點雞肋,並不能實現真正的非同步,需要阻塞的獲取結果,或者不斷的輪詢。

通常我們希望當執行緒執行完一些耗時的任務後,能夠自動的通知我們結果,很遺憾這在原生jdk1.8之前是不支援的,但是我們可以通過第三方的庫實現真正的非同步回撥。

/**
 * jdk1.8之前的Future
 * @author Administrator
 */
public class JavaFuture {
	public static void main(String[] args) throws Throwable, ExecutionException {
		ExecutorService executor = Executors.newFixedThreadPool(1);
		Future<String> f = executor.submit(new Callable<String>() {
 
			@Override
			public String call() throws Exception {
				System.out.println("task started!");
				longTimeMethod();
				System.out.println("task finished!");
				return "hello";
			}
		});
 
		//此處get()方法阻塞main執行緒
		System.out.println(f.get());
		System.out.println("main thread is blocked");
	}
}

如果想獲得耗時操作的結果,可以通過get()方法獲取,但是該方法會阻塞當前執行緒,我們可以在做完剩下的某些工作的時候呼叫get()方法試圖去獲取結果。

也可以呼叫非阻塞的方法isDone來確定操作是否完成,isDone這種方式有點兒類似下面的過程:

2. jdk1.8開始的Future

直到jdk1.8才算真正支援了非同步操作,jdk1.8中提供了lambda表示式,使得java向函式式語言又靠近了一步。藉助jdk原生的CompletableFuture可以實現非同步的操作,同時結合lambada表示式大大簡化了程式碼量。程式碼例子如下:

package netty_promise;
 
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
 
/**
 * 基於jdk1.8實現任務非同步處理
 * @author Administrator
 */
public class JavaPromise {
	public static void main(String[] args) throws Throwable, ExecutionException {
		// 兩個執行緒的執行緒池
		ExecutorService executor = Executors.newFixedThreadPool(2);
		//jdk1.8之前的實現方式
		CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
			@Override
			public String get() {
				System.out.println("task started!");
				try {
					//模擬耗時操作
					longTimeMethod();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				return "task finished!";
			}
		}, executor);
 
		//採用lambada的實現方式
		future.thenAccept(e -> System.out.println(e + " ok"));
		
		System.out.println("main thread is running");
	}
}

實現方式類似下圖:

3. Spring的非同步方法

先把longTimeMethod 封裝到Spring的非同步方法中,這個非同步方法的返回值是Future的例項。這個方法一定要寫在Spring管理的類中,注意註解@Async。

@Service
public class AsynchronousService{
  @Async
  public Future springAsynchronousMethod(){
    Integer result = longTimeMethod();
    return new AsyncResult(result);
  }
}

其他類呼叫這個方法。這裡注意,一定要其他的類,如果在同類中呼叫,是不生效的。

@Autowired
private AsynchronousService asynchronousService;

public void useAsynchronousMethod(){
    Future future = asynchronousService.springAsynchronousMethod();
    future.get(1000, TimeUnit.MILLISECONDS);
}

其實Spring只不過在原生的Future中進行了一次封裝,我們最終獲得的還是Future例項。

4. Java如何將非同步呼叫轉為同步

換句話說,就是需要在非同步呼叫過程中,持續阻塞至獲得呼叫結果。

    • 使用wait和notify方法
    • 使用條件鎖
    • Future
    • 使用CountDownLatch
    • 使用CyclicBarrier