天天看點

Java 如何避免線程阻塞?

作者:IT三寶

Java中可以通過以下幾種方式來避免線程阻塞:

  1. 異步程式設計:使用異步程式設計可以避免線程阻塞,通過回調函數或者Future/Promise等方式來處理異步結果。
  2. 多線程并發:将任務拆分成多個子任務,使用多個線程并發執行,可以減少單個線程的阻塞時間。
  3. 非阻塞IO:使用NIO(New IO)等非阻塞IO方式,可以避免線程在IO操作時阻塞。
  4. 逾時機制:對于可能會阻塞的操作,可以設定逾時時間,逾時後自動結束操作,避免線程長時間阻塞。
  5. 線程池:使用線程池可以有效避免線程阻塞,通過線程池可控制線程的數量和執行順序,避免線程阻塞和資源浪費。

下面是一些具體例子

異步程式設計:

使用CompletableFuture實作異步程式設計:

CompletableFuture.supplyAsync(() -> {
    // 異步執行的任務
    return result;
}).thenAcceptAsync(result -> {
    // 異步執行完後的回調函數
});           

多線程并發:

使用ExecutorService實作多線程并發:

ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            // 處理任務
        }
    });
}
executorService.shutdown();           

非阻塞IO:

使用NIO實作非阻塞IO:

Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(port));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
    selector.select();
    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iterator = selectionKeys.iterator();
    while (iterator.hasNext()) {
        SelectionKey selectionKey = iterator.next();
        if (selectionKey.isAcceptable()) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            socketChannel.read(buffer);
            buffer.flip();
            socketChannel.write(buffer);
            buffer.clear();
        }
        iterator.remove();
    }
}           

逾時機制:

使用Future和TimeUnit實作逾時機制:

Future<String> future = executorService.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        // 執行任務
        return result;
    }
});
try {
    String result = future.get(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (TimeoutException e) {
    future.cancel(true);
    e.printStackTrace();
}           

逾時機制:

使用Future和TimeUnit實作逾時機制:

Future<String> future = executorService.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        // 執行任務
        return result;
    }
});
try {
    String result = future.get(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (TimeoutException e) {
    future.cancel(true);
    e.printStackTrace();
}           

線程池:

使用ThreadPoolExecutor實作線程池:

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
        10, // 核心線程數
        20, // 最大線程數
        60, // 線程池空閑時間
        TimeUnit.SECONDS, // 時間機關
        new LinkedBlockingQueue<Runnable>(), // 任務隊列
        new ThreadPoolExecutor.AbortPolicy() // 拒絕政策
);
for (int i = 0; i < 100; i++) {
    threadPoolExecutor.execute(new Runnable() {
        @Override
        public void run() {
            // 處理任務
        }
    });
}
threadPoolExecutor.shutdown();           

這個可以清晰的了解到阻塞、同步、異步的現象、概念和特征以及優缺點。有大量的案例可以快速了解每種通信架構的使用。

大廠面試之IO模式詳解(AIO,BIO,NIO)

繼續閱讀