導航:首頁 > 編程語言 > java線程池線程數

java線程池線程數

發布時間:2023-05-30 10:16:34

java線程池ExecutorService,裡面有多少空餘線程,怎麼看

ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(100);//創建線程池,這種線程池固定了線埋睜程數量
pool.getActiveCount();//獲取態液派活動的線程帆賀數量
100-活動數量就是空閑數量

❷ Java線程池

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

❸ Java的線程池,如何設定保留的最小線程數和固定的隊列容量

創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。在任意點,在大多數 nThreads 線程會處於處理任務的活動狀態。如果在所有線程處於活動狀態時提交附加任務,則在有可用線程之前,附加任務將在隊列中等待。如果在關閉前的執行期間由於失敗而導致任何線程終止,那麼一個新線程將代替它執行後續的任務(如果需要)。在某個線程被顯式地關閉之前,池中的線程將一直存在。

看代碼:
Java代碼 收藏代碼
package test;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecutorTest {
public static void main(String args[]) {
Random random = new Random();
//產生一個 ExecutorService 對象,這個對象帶有一個大小為 poolSize 的線程池,若任務數量大於 poolSize ,任務會被放在一個 queue 里順序執行。
ExecutorService executor = Executors.newFixedThreadPool(3);
// 判斷可是線程池可以結束
int waitTime = 500;
for (int i = 0; i < 10; i++) {
String name = "線程 " + i;
int time = random.nextInt(1000);
waitTime += time;
Runnable runner = new ExecutorThread(name, time);
System.out.println("增加: " + name + " / " + time);
executor.execute(runner);
}
try {
Thread.sleep(waitTime);
executor.shutdown();
executor.awaitTermination(waitTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {
}
}
}

class ExecutorThread implements Runnable {
private final String name;
private final int delay;
public ExecutorThread(String name, int delay) {
this.name = name;
this.delay = delay;
}
public void run() {
System.out.println("啟動: " + name);
try {
Thread.sleep(delay);
} catch (InterruptedException ignored) {
}
System.out.println("完成: " + name);
}
}

❹ java線程池ExecutorService,裡面有多少空餘線程,怎麼看

ExecutorService是個介面,如果你是用Executors靜態方法李絕生產的畢擾段實例,見具體實現。
比如:
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
那手譽它是ThreadPoolExecutor的實例,你可以看它的方法,如getActiveCount(),getPoolSize()等。

❺ Java 線程池的問題

你的理解沒毛病。

核心線程數(corePoolSize):核心線程會一直存活,即使沒有任務需要處理。當線程數小於核心線程數時,即使現有的線程空閑,線程池也蠢答會優先創建新線程來處理任務,而不是直接交給現有的線程處理。

最大線程數(maxPoolSize):當線程數大於或等於核心線程,且任務隊列已滿時,線改叢程池會創建新的線程,直到線程數量達到maxPoolSize。如果線程數已等於maxPoolSize,且任務隊列已滿,則已超出線程池的處理能力,線程池會拒絕處理任務而拋出異常。

線程池按以下行為執行任務

❻ 什麼是java線程池

找的資料,你看一下吧:x0dx0a多線程技術主要解決處理器單元內多個線程執行的問題,褲嘩它可以顯著減讓汪少處理器單元的閑置時間,增加處理器單元的吞吐能力。x0dx0a x0dx0a 假設一個伺服器完成一項任務所需時間為:T1 創建線程時間,T2 在線程中執行任務的時間,T3 銷毀線程時間。x0dx0a x0dx0a 如果:T1 + T3 遠大於 T2,則可以採用線程池,以提高坦純仔伺服器性能。x0dx0a 一個線程池包括以下四個基本組成部分:x0dx0a 1、線程池管理器(ThreadPool):用於創建並管理線程池,包括 創建線程池,銷毀線程池,添加新任務;x0dx0a 2、工作線程(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,可以循環的執行任務;x0dx0a 3、任務介面(Task):每個任務必須實現的介面,以供工作線程調度任務的執行,它主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀態等;x0dx0a 4、任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩沖機制。x0dx0a x0dx0a 線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程序性能的。它把T1,T3分別安排在伺服器程序的啟動和結束的時間段或者一些空閑的時間段,這樣在伺服器程序處理客戶請求時,不會有T1,T3的開銷了。x0dx0ax0dx0a 線程池不僅調整T1,T3產生的時間段,而且它還顯著減少了創建線程的數目,看一個例子:x0dx0ax0dx0a 假設一個伺服器一天要處理50000個請求,並且每個請求需要一個單獨的線程完成。在線程池中,線程數一般是固定的,所以產生線程總數不會超過線程池中線程的數目,而如果伺服器不利用線程池來處理這些請求則線程總數為50000。一般線程池大小是遠小於50000。所以利用線程池的伺服器程序不會為了創建50000而在處理請求時浪費時間,從而提高效率。

❼ java web 有多個threadpool時候如何設定線程數

底層的實現原理基本一樣: new線程池的時候生成一個任務隊列(blockQueue<Runnable>),第一次執行execute()或者submit()方法時會創建一個循環的線程,用於反復讀取隊列中的任務並執行之(ps:第一次提交的任務是不用進入任務隊列,由剛創建的線知仿程直接執行 ),後續的 execute()或者submit()操作則直接提交Runnable任務到隊列里.隊列為空時,循環線程就會被blockQueue的take()方法阻塞住.

SingleThreadExecutor其實是FixedThreadPool的一個特例,SingleThreadExecutor指定對於同一個隊列只有一個線程去循環讀取隊列任務並執行, FiexedThreadPool則可以為同一隊列指定多個線程去循環讀取隊列任務並執行.
newFixedThreadPool(10)會產生10個線程去讀取同一個任務隊列,但這10個線程不是同時產生,而是提交一個任務(即執行一次execute()或者submit()方法)產生一個,當提交的任務數量超過10個,第11個任務直接搭蘆纖提交到blockQueue<Runnable>隊列里,然後由這10個線程中的某個線程去獲取並執行該任務.FixedThreadPool產生的10個線程以後也不會被回收成9個,更不可能增加到11個.

CacheThreadPool不指定具體數量的線程去讀取並只執行任務隊列中的任務,但是它有個最大線程數(Integer.MAX_VALUE=2的32次-1), 當 任務隊列飽和無法插入新任務時,會自動生成一個新的線程去執行新插入的任務,並參與讀取飽和的任務隊列並執行.如果高峰期生成了10個線程,低谷期只需要一個線程來執行,其餘的9個線程在存活一段時間後就會被終止.存活時間默認是一分鍾.這一點要和FixedThreadPool區分.
ScheledThreadPool線程池線程數量也需要預先指定,它的主要特點是按計劃延時讀取並執行隊列任務

無論何種線程,當任務隊列增加任務的速度大於隊列讀取執行的速度時,就可能產生任務丟失的情況,丟失的概率由低到高依次是
CacheThreadPool > newFixedThreadPool > SingleThreadExecutor,這個很好理解.這種情況下,程序默認都會向外拋嘩神出RejectedExecutionException異常
new 線程池的時候另一個構造參數 ThreadFactory,主要用途就是對提交的任務做個簡單的封裝.

❽ java應用中可以有多少 線程池

可以有多少個線程池的問題,如果假設每個線程池中只有一個線程,那麼就轉化為應用中可以有多少個線程
這個跟jvm的配置,操作系統相關
每個線程在jvm中默認是分配1m大小的內存,當然可以調整,因此這個可用線程數的多少跟你操作系統目前剩餘內存有關
同時一個操作系統中最大的線程數一般為3000-5000,當然理論值是這樣,如果線程數過大,會有調度方面的延遲,導致大數量級的線程反而比小數量級的線程運行得更慢。

❾ 合理使用線程池以及線程變數

背景

隨著計算技術的不斷發展,3納米製程晶元已進入試產階段,摩爾定律在現有工藝下逐漸面臨巨大的物理瓶頸,通過多核處理器技術來提升伺服器的性能成為提升算力的主要方向。

在伺服器領域,基於java構建的後端伺服器占據著領先地位,因此,掌握java並發編程技術,充分利用CPU的並發處理能力是一個開發人員必修的基本功,本文結合線程池源碼和實踐,簡要介紹了線程池和線程變數的使用。

線程池概述

線程池是一種「池化」的線程使用模式,通過創建一定數量的線程,讓這些線程處於就緒狀態來提高系統響應速度,在線程使用完成後歸還到線程池來達到重復利用的目標,從而降低系統資源的消耗。

總體來說,線程池有如下的優勢:

線程池的使用

在java中,線程池的實現類是ThreadPoolExecutor,構造函數如下:

可以通過 new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,handler)來創建一個線程池。

在構造函數中,corePoolSize為線程池核心線程數。默認情況下,核心線程會一直存活,但是當將allowCoreThreadTimeout設置為true時,核心線程超時也會回收。

在構造函數中,maximumPoolSize為線程池所能容納的最首高模大線程數。

在構造函數中,keepAliveTime表示線程閑置超時時長。如果線程閑置時間超過該時長,非核心線程就會被回收。如果將allowCoreThreadTimeout設置為true時,核心線程也會超時回收。

在構造函數中,timeUnit表示線程閑置超時時長的時間單位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。

在構造函數中,blockingQueue表示任務隊列,線程池任務隊列的常用實現類有:

在構造函數中,threadFactory表示線程工廠。用於指定為線程池創建新線程的方式,threadFactory可以設置線程名稱、線程組、優先順序等參數。如通過Google工具包可以設置線程池裡的線程名:

在構造函數中,rejectedExecutionHandler表示拒絕策略。當達到最大線程數且隊列任務已滿時需要執行的拒絕策略,常見的拒絕策略如下:


ThreadPoolExecutor線程池有如下幾種狀態:



線程池提交一個任務時任務調度的主要步驟如下:

核心代碼如下:


Tomcat 的整體架構包含連接器和容器兩大部分,其中連接器負責與外部通信,容器負責內部邏輯處理。在連接器中:

Tomcat為了實現請者緩求的快念鉛速響應,使用線程池來提高請求的處理能力。下面我們以HTTP非阻塞I/O為例對Tomcat線程池進行簡要的分析。

在Tomcat中,通過AbstractEndpoint類提供底層的網路I/O的處理,若用戶沒有配置自定義公共線程池,則AbstractEndpoint通過createExecutor方法來創建Tomcat默認線程池。

核心部分代碼如下:

其中,TaskQueue、ThreadPoolExecutor分別為Tomcat自定義任務隊列、線程池實現。

Tomcat自定義線程池繼承於java.util.concurrent.ThreadPoolExecutor,並新增了一些成員變數來更高效地統計已經提交但尚未完成的任務數量(submittedCount),包括已經在隊列中的任務和已經交給工作線程但還未開始執行的任務。

Tomcat在自定義線程池ThreadPoolExecutor中重寫了execute()方法,並實現對提交執行的任務進行submittedCount加一。Tomcat在自定義ThreadPoolExecutor中,當線程池拋出RejectedExecutionException異常後,會調用force()方法再次向TaskQueue中進行添加任務的嘗試。如果添加失敗,則submittedCount減一後,再拋出RejectedExecutionException。

在Tomcat中重新定義了一個阻塞隊列TaskQueue,它繼承於LinkedBlockingQueue。在Tomcat中,核心線程數默認值為10,最大線程數默認為200, 為了避免線程到達核心線程數後後續任務放入隊列等待,Tomcat通過自定義任務隊列TaskQueue重寫offer方法實現了核心線程池數達到配置數後線程的創建。

具體地,從線程池任務調度機制實現可知,當offer方法返回false時,線程池將嘗試創建新新線程,從而實現任務的快速響應。TaskQueue核心實現代碼如下:

Tomcat中通過自定義任務線程TaskThread實現對每個線程創建時間的記錄;使用靜態內部類WrappingRunnable對Runnable進行包裝,用於對StopPooledThreadException異常類型的處理。

Executors常用方法有以下幾個:

Executors類看起來功能比較強大、用起來還比較方便,但存在如下弊端

使用線程時,可以直接調用 ThreadPoolExecutor 的構造函數來創建線程池,並根據業務實際場景來設置corePoolSize、blockingQueue、RejectedExecuteHandler等參數。

使用局部線程池時,若任務執行完後沒有執行shutdown()方法或有其他不當引用,極易造成系統資源耗盡。

在工程實踐中,通常使用下述公式來計算核心線程數:

nThreads=(w+c)/c*n*u=(w/c+1)*n*u

其中,w為等待時間,c為計算時間,n為CPU核心數(通常可通過 Runtime.getRuntime().availableProcessors()方法獲取),u為CPU目標利用率(取值區間為[0, 1]);在最大化CPU利用率的情況下,當處理的任務為計算密集型任務時,即等待時間w為0,此時核心線程數等於CPU核心數。

上述計算公式是理想情況下的建議核心線程數,而不同系統/應用在運行不同的任務時可能會有一定的差異,因此最佳線程數參數還需要根據任務的實際運行情況和壓測表現進行微調。

為了更好地發現、分析和解決問題,建議在使用多線程時增加對異常的處理,異常處理通常有下述方案:

為了實現優雅停機的目標,我們應當先調用shutdown方法,調用這個方法也就意味著,這個線程池不會再接收任何新的任務,但是已經提交的任務還會繼續執行。之後我們還應當調用awaitTermination方法,這個方法可以設定線程池在關閉之前的最大超時時間,如果在超時時間結束之前線程池能夠正常關閉則會返回true,否則,超時會返回false。通常我們需要根據業務場景預估一個合理的超時時間,然後調用該方法。

如果awaitTermination方法返回false,但又希望盡可能在線程池關閉之後再做其他資源回收工作,可以考慮再調用一下shutdownNow方法,此時隊列中所有尚未被處理的任務都會被丟棄,同時會設置線程池中每個線程的中斷標志位。shutdownNow並不保證一定可以讓正在運行的線程停止工作,除非提交給線程的任務能夠正確響應中斷。

ThreadLocal線程變數概述

ThreadLocal類提供了線程本地變數(thread-local variables),這些變數不同於普通的變數,訪問線程本地變數的每個線程(通過其get或set方法)都有其自己的獨立初始化的變數副本,因此ThreadLocal沒有多線程競爭的問題,不需要單獨進行加鎖。

ThreadLocal的原理與實踐

對於ThreadLocal而言,常用的方法有get/set/initialValue 3個方法。

眾所周知,在java中SimpleDateFormat有線程安全問題,為了安全地使用SimpleDateFormat,除了1)創建SimpleDateFormat局部變數;和2)加同步鎖 兩種方案外,我們還可以使用3)ThreadLocal的方案:

Thread 內部維護了一個 ThreadLocal.ThreadLocalMap 實例(threadLocals),ThreadLocal 的操作都是圍繞著 threadLocals 來操作的。

從JDK源碼可見,ThreadLocalMap中的Entry是弱引用類型的,這就意味著如果這個ThreadLocal只被這個Entry引用,而沒有被其他對象強引用時,就會在下一次GC的時候回收掉。

EagleEye(鷹眼)作為全鏈路監控系統在集團內部被廣泛使用,traceId、rpcId、壓測標等信息存儲在EagleEye的ThreadLocal變數中,並在HSF/Dubbo服務調用間進行傳遞。EagleEye通過Filter將數據初始化到ThreadLocal中,部分相關代碼如下:

在EagleEyeFilter中,通過EagleEyeRequestTracer.startTrace方法進行初始化,在前置入參轉換後,通過startTrace重載方法將鷹眼上下文參數存入ThreadLocal中,相關代碼如下:

EagleEyeFilter在finally代碼塊中,通過EagleEyeRequestTracer.endTrace方法結束調用鏈,通過clear方法將ThreadLocal中的數據進行清理,相關代碼實現如下:

在某權益領取原有鏈路中,通過app打開一級頁面後才能發起權益領取請求,請求經過淘系無線網關(Mtop)後到達服務端,服務端通過mtop sdk獲取當前會話信息。

在XX項目中,對權益領取鏈路進行了升級改造,在一級頁面請求時,通過服務端同時發起權益領取請求。具體地,服務端在處理一級頁面請求時,同時通過調用hsf/bbo介面來進行權益領取,因此在發起rpc調用時需要攜帶用戶當前會話信息,在服務提供端將會話信息進行提取並注入到mtop上下文,從而才能通過mtop sdk獲取到會話id等信息。某開發同學在實現時,因ThreadLocal使用不當造成下述問題:

【問題1:權益領取失敗分析】

在權益領取服務中,該應用構建了一套高效和線程安全的依賴注入框架,基於該框架的業務邏輯模塊通常抽象為xxxMole形式,Mole間為網狀依賴關系,框架會按依賴關系自動調用init方法(其中,被依賴的mole 的init方法先執行)。

在應用中,權益領取介面的主入口為CommonXXApplyMole類,CommonXXApplyMole依賴XXSessionMole。當請求來臨時,會按依賴關系依次調用init方法,因此XXSessionMole的init方法會優先執行;而開發同學在CommonXXApplyMole類中的init方法中通過調用recoverMtopContext()方法來期望恢復mtop上下文,因recoverMtopContext()方法的調用時機過晚,從而導致XXSessionMole模塊獲取不到正確的會話id等信息而導致權益領取失敗。

【問題2:臟數據分析】

權益領取服務在處理請求時,若當前線程曾經處理過權益領取請求,因ThreadLocal變數值未被清理,此時XXSessionMole通過mtop SDK獲取會話信息時得到的是前一次請求的會話信息,從而造成臟數據。

【解決方案】

在依賴注入框架入口處AbstractGate#visit(或在XXSessionMole中)通過recoverMtopContext方法注入mtop上下文信息,並在入口方法的finally代碼塊清理當前請求的threadlocal變數值。

若使用強引用類型,則threadlocal的引用鏈為:Thread -> ThreadLocal.ThreadLocalMap -> Entry[] -> Entry -> key(threadLocal對象)和value;在這種場景下,只要這個線程還在運行(如線程池場景),若不調用remove方法,則該對象及關聯的所有強引用對象都不會被垃圾回收器回收。

若使用static關鍵字進行修飾,則一個線程僅對應一個線程變數;否則,threadlocal語義變為perThread-perInstance,容易引發內存泄漏,如下述示例:

在上述main方法第22行debug,可見線程的threadLocals變數中有3個threadlocal實例。在工程實踐中,使用threadlocal時通常期望一個線程只有一個threadlocal實例,因此,若不使用static修飾,期望的語義發生了變化,同時易引起內存泄漏。

如果不執行清理操作,則可能會出現:

建議使用try...finally 進行清理。

我們在使用ThreadLocal時,通常期望的語義是perThread,若不使用static進行修飾,則語義變為perThread-perInstance;在線程池場景下,若不用static進行修飾,創建的線程相關實例可能會達到 M * N個(其中M為線程數,N為對應類的實例數),易造成內存泄漏(https://errorprone.info/bugpattern/ThreadLocalUsage)。

在應用中,謹慎使用ThreadLocal.withInitial(Supplier<? extends S> supplier)這個工廠方法創建ThreadLocal對象,一旦不同線程的ThreadLocal使用了同一個Supplier對象,那麼隔離也就無從談起了,如:

總結

在java工程實踐中,線程池和線程變數被廣泛使用,因線程池和線程變數的不當使用經常造成安全生產事故,因此,正確使用線程池和線程變數是每一位開發人員必須修煉的基本功。本文從線程池和線程變數的使用出發,簡要介紹了線程池和線程變數的原理和使用實踐,各開發人員可結合最佳實踐和實際應用場景,正確地使用線程和線程變數,構建出穩定、高效的java應用服務。

❿ java如何確定線程池最多線程的大小

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);

參數:

corePoolSize(線程池的基本大小):當提交一個任務到線程池時,線程池會創建一個線程來執行任務,即使其他空閑的基本線程能夠執行新任務也會創建線程,等到需要執行的任務數大於線程池基本大小時就不再創建。如果調用了線程池的prestartAllCoreThreads方法,線程池會提前創建並啟動所有基本線程。
runnableTaskQueue(任務隊列):用於保存等待執行的任務的阻塞隊列。 可以選擇以下幾個阻塞隊列。
ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。
LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列。
SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。
PriorityBlockingQueue:一個具有優先順序的無限阻塞隊列。
maximumPoolSize(線程池最大大小):線程池允許創建的最大線程數。如果隊列滿了,並且已創建的線程數小於最大線程數,則線程池會再創建新的線程執行任棚沖務。值得注意的是如果使用了無界的任務隊列這個參數就沒什麼效果。
ThreadFactory:用於設置創建線程的工廠,可以通過線程工廠給每個創建出來的線程設置更有意義的名字。
RejectedExecutionHandler(飽和策略):當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採取一種策略處理提交的新任務。這個策略默認情況下是AbortPolicy,表示無法處理新任務時拋出異常。以下是JDK1.5提供的四種策乎和猜略。
AbortPolicy:直接拋出異常。
CallerRunsPolicy:只用調用者所在歲型線程來運行任務。
DiscardOldestPolicy:丟棄隊列里最近的一個任務,並執行當前任務。
DiscardPolicy:不處理,丟棄掉。
當然也可以根據應用場景需要來實現RejectedExecutionHandler介面自定義策略。如記錄日誌或持久化不能處理的任務。
keepAliveTime(線程活動保持時間):線程池的工作線程空閑後,保持存活的時間。所以如果任務很多,並且每個任務執行的時間比較短,可以調大這個時間,提高線程的利用率。
TimeUnit(線程活動保持時間的單位):可選的單位有天(DAYS),小時(HOURS),分鍾(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。

閱讀全文

與java線程池線程數相關的資料

熱點內容
文件加密了為啥發不出去了 瀏覽:457
單片機調節馬達 瀏覽:743
鏡花pdf 瀏覽:610
廣西民族大學app忘記密碼怎麼辦 瀏覽:374
學生伺服器是什麼意思 瀏覽:533
如何下載快切app 瀏覽:723
如何將電腦c盤文件加密 瀏覽:886
嵌入式為什麼linux 瀏覽:553
c語言編譯器屬於系統軟體 瀏覽:725
android如何斷點調試 瀏覽:722
圖解韓語pdf 瀏覽:302
sas查各文件夾空間大小 瀏覽:454
python腳本檢查埠 瀏覽:960
催眠解壓視頻泡沫 瀏覽:309
雲伺服器部署系統 瀏覽:879
惡意加密別人的文件犯法 瀏覽:833
漢語語法pdf 瀏覽:158
詞法分析編譯原理論文 瀏覽:271
電腦文件夾還原方法 瀏覽:534
安卓包如何成為文檔 瀏覽:949