1.線程通信基礎
- 生成者消費者
2.AsyncTask
- FutureTask
- 線程池
- 問題和缺點
3.HandlerThread
- 優點
- 例子
- IntentService
- 原理和使用
- 優點
5.Loader
- 優點
- 例子
1.線程通信基礎
1.1.普通的生產者消費者模式
public class ThreadTest1 {
//產品
static class ProductObject{
//線程操作變量可見
public volatile static String value;
}
//生產者線程
static class Producer extends Thread{
@Override
public void run() {
//不斷生產產品
while(true){
if(ProductObject.value == null){
ProductObject.value = "NO:"+System.currentTimeMillis();
System.out.println("生產產品:"+ProductObject.value);
}
}
}
}
//消費者線程
static class Consumer extends Thread{
@Override
public void run() {
while(true){
if(ProductObject.value != null){
System.out.println("消費產品:"+ProductObject.value);
ProductObject.value = null;
}
}
}
}
public static void main(String[] args) {
new Producer().start();
new Consumer().start();
}
}
當兩個線程對同一個值value操作的時候,在每個線程中都會有一個私有空間保存這個值,即每個線程分別有一個value,假如A線程修改了value,B是不知道A修改了。
1.boolean value=true
生成者線程中vaule修改爲false,消費者中的value任然爲true。
如何修改:給修改值加上volatile,就能保證同步。
volatile boolean value=true;
image.png
1.2.優化
但是volatile的這種操作也會帶來一個問題,就是消費者和生產者線程需要不斷的去判斷值是否消費,這樣也會帶來性能消耗,這裏引入了鎖的概念。
image.png
public class ThreadTest1 {
//產品
static class ProductObject{
//線程操作變量可見
public volatile static String value;
}
//生產者線程
static class Producer extends Thread{
Object lock;
public Producer(Object lock) {
this.lock = lock;
}
@Override
public void run() {
//不斷生產產品
while(true){
synchronized (lock) { //互斥鎖
//產品還沒有被消費,等待
if(ProductObject.value != null){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//產品已經消費完成,生產新的產品
ProductObject.value = "NO:"+System.currentTimeMillis();
System.out.println("生產產品:"+ProductObject.value);
lock.notify(); //生產完成,通知消費者消費
}
}
}
}
//消費者線程
static class Consumer extends Thread{
Object lock;
public Consumer(Object lock) {
this.lock = lock;
}
@Override
public void run() {
while(true){
synchronized (lock) {
//沒有產品可以消費
if(ProductObject.value == null){
//等待,阻塞
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消費產品:"+ProductObject.value);
ProductObject.value = null;
lock.notify(); //消費完成,通知生產者,繼續生產
}
}
}
}
public static void main(String[] args) {
Object lock = new Object();
new Producer(lock).start();
new Consumer(lock).start();
}
}
2.AsyncTask
Android的刷新頻率是60fps,如果低於25fps,就會感覺有卡頓的現象。
優化點:減少主線程的負擔,創建子線程進行處理。那麼就涉及到子線程和主線程的通信。
子線程和主線程的通信方式:
- AsyncTask
- Handler
2.1.FutureTask
Callable:可以返回結果,Runable是無法獲取結果的
Future
在普通的線程中(比如上面的例子),異步任務執行的結果,主線程是無法輕易獲取。
FutureTask是可以獲取到異步線程中的結果。
Java FutureTask 異步任務操作提供了便利性:
- 1.獲取異步任務的返回值
- 2.監聽異步任務的執行完畢
- 3.取消異步任務
public AsyncTask() {
// 實現了Callable
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams); //子線程
Binder.flushPendingCommands();
return postResult(result);
}
};
//實現了RunnableFuture
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get()); //主線程
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
FutureTask模式實現:
public class FutureTest1 {
public static void main(String[] args) {
Task work = new Task();
FutureTask<Integer> future = new FutureTask<Integer>(work){
//異步任務執行完成,回調
@Override
protected void done() {
try {
System.out.println("done:"+get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
//線程池(使用了預定義的配置)
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(future);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
//取消異步任務
future.cancel(true);
try {
//阻塞,等待異步任務執行完畢
System.out.println(future.get()); //獲取異步任務的返回值
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
//異步任務
static class Task implements Callable<Integer>{
//返回異步任務的執行結果
@Override
public Integer call() throws Exception {
int i = 0;
for (; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + "_"+i);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return i;
}
}
}
doBackground()在call方法中執行
call的返回值在Future的done方法中獲取
->onPostExecute
new MyTask().execute();
2.2.執行流程:
->onPostExecute
new MyTask().execute();
實例化:
new AsyncTask() -> new FutureTask()
執行:
Executor.execute(mFuture) -> SerialExecutor.myTasks(隊列)
-> (線程池)THREAD_POOL_EXECUTOR.execute
線程池中的所有線程,爲了執行異步任務
2.3.線程池:
線程池中的所有線程,爲了執行異步任務
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,
KEEP_ALIVE,TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
參數 | 意義 |
---|---|
CORE_POOL_SIZE | 核心線程數 |
MAXIMUM_POOL_SIZE | 最大線程數量 |
KEEP_ALIVE | 1s閒置回收 |
TimeUnit.SECONDS | 時間單位 |
sPoolWorkQueue | 異步任務隊列 |
sThreadFactory | 線程工廠 |
- 如果當前線程池中的數量小於corePoolSize,創建並添加的任務。
- 如果當前線程池中的數量等於corePoolSize,緩衝隊列 workQueue未滿,那麼任務被放入緩衝隊列、等待任務調度執行。
- 如果當前線程池中的數量大於corePoolSize,緩衝隊列workQueue已滿,並且線程池中的數量小於maximumPoolSize,新提交任務會創建新線程執行任務。
- 如果當前線程池中的數量大於corePoolSize,緩衝隊列workQueue已滿,並且線程池中的數量等於maximumPoolSize,新提交任務由Handler處理。
- 當線程池中的線程大於corePoolSize時,多餘線程空閒時間超過keepAliveTime時,會關閉這部分線程。
image.png
最終AsyncTask執行的任務是在線程池中執行的,如果創建大量的線程,會出現線程堵塞的現象(FC的風險)。
2.4.問題和缺點:參考
- 1.生命週期;
- 2.內存泄漏;
- 3.結果丟失;
- 4.並行還是串行;
- 5.線程池不夠導致拋出異常:線程池中已經有128個線程,緩衝隊列已滿,如果此時向線程提交任務,將會拋出RejectedExecutionException。過多的線程會引起大量消耗系統資源和導致應用FC的風險;
- 6.異步任務中只能幹一件事情,一個線程只能幹一件事情。
添加任務到線程池的過程是串行,在線程池中執行時是並行。
public class AsyncTaskTest {
public static void main(String[] args) {
int CPU_COUNT = Runtime.getRuntime().availableProcessors(); //可用的CPU個數
int CORE_POOL_SIZE = CPU_COUNT + 1; //5
int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; //9
int KEEP_ALIVE = 1;
//任務隊列(128)
final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
//線程工廠
ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
String name = "Thread #" + mCount.getAndIncrement();
System.out.println(name);
return new Thread(r, name);
}
};
//線程池
Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
//執行異步任務
//如果當前線程池中的數量大於corePoolSize,緩衝隊列workQueue已滿,
//並且線程池中的數量等於maximumPoolSize,新提交任務由Handler處理。
//RejectedExecutionException
for (int i = 0; i < 200; i++) {
//相當於new AsyncTask().execute();
THREAD_POOL_EXECUTOR.execute(new MyTask());
}
}
static class MyTask implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
/*while(true){
try {
System.out.println(Thread.currentThread().getName());
//Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}*/
}
}
}
AsyncTask可以自定義線程池,風險是:太多線程可能導致內存消耗太多。
//1.使用的默認線程池
task = new MyTask();
task.execute();
//2.線程池擴容,自定義線程池
Executor exec = Executors.newScheduledThreadPool(25);
for (int i = 0; i < 200; i++) {
new MyTask().executeOnExecutor(exec);
}
在Activity的onDestroy中task.cancel(true),並不能真正取消線程執行。
AsyncTask的handler用到的的Looper是主線程的,如果任務太多,在主線程中進行輪詢,會導致UI線程有卡頓的現象。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
3.HandlerThread
那如果對異步任務的輪詢放在子線程中處理,會不會好點呢。那麼就引出了HandlerThread,他就是一個Thread
public class HandlerThreadActivity1 extends Activity {
HandlerThread fetchThread = new HandlerThread("fetching_thread");
Handler fetchHandler;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
tv = (TextView) findViewById(R.id.tv);
//啓動線程
fetchThread.start();
//通過fetchHandler發送的消息,會被fetchThread線程創建的輪詢器拉取到
fetchHandler = new Handler(fetchThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//模擬訪問網絡延遲
SystemClock.sleep(1000);
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("泰銖匯率:"+new Random().nextInt(10));
}
});
//循環執行
fetchHandler.sendEmptyMessage(1);
}
};
}
@Override
protected void onResume() {
super.onResume();
fetchHandler.sendEmptyMessage(1);
}
@Override
protected void onStop() {
super.onStop();
fetchThread.quit(); //取消
}
}
3.1.HandlerThread的優點:
- 1.減輕主線程的壓力,提高UI的流暢度(減少主線程的輪詢);
- 2.可以處理多個任務,開啓一個線程起到多個線程的作用(原理是:looper共享)
3.2.例子
1.打開相機
2.預覽回調(編碼)
public class HandlerThreadActivity2 extends Activity implements Callback {
static final String TAG = "jason";
Camera mCamera;
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;
byte[] buffers;
HandlerThread mHandlerThread = new HandlerThread("my_handlerthread");
Handler subHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread2);
surfaceView = (SurfaceView) findViewById(R.id.surface_view);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
}
class MyTask implements Runnable, PreviewCallback{
@Override
public void run() {
//打開相機
//子線程中打開
Log.d("jason", Thread.currentThread().getName() + "_open");
mCamera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
try {
mCamera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
Camera.Parameters parameters = mCamera.getParameters();
//設置相機參數
parameters.setPreviewSize(480, 320); //預覽畫面寬高
mCamera.setParameters(parameters);
//獲取預覽圖像數據
buffers = new byte[480 * 320 * 4];
mCamera.addCallbackBuffer(buffers);
mCamera.setPreviewCallbackWithBuffer(this);
mCamera.startPreview();
Log.d(TAG, Thread.currentThread().getName()+ "_run");
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if(mCamera != null){
mCamera.addCallbackBuffer(buffers);
//編碼
Log.d(TAG, Thread.currentThread().getName()+ "_onPreviewFrame");
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mHandlerThread.start();
subHandler = new Handler(mHandlerThread.getLooper());
subHandler.post(new MyTask());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
如果用AsyncTask,onPreviewFrame的執行就到了主線程。爲什麼呢?
相機中的代碼Camera:
異步任務的Looper,使用的MainLooper
Handler.handleMessage的執行,一定在它的Looper線程中
onPreviewFrame的執行,在Camera所持有的Looper線程中執行
new Camera -> looper -> EventHandler.handleMessage -> onPreviewFrame
private int cameraInitVersion(int cameraId, int halVersion) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
mPostviewCallback = null;
mUsingPreviewAllocation = false;
mZoomListener = null;
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
return native_setup(new WeakReference<Camera>(this), cameraId, halVersion,
ActivityThread.currentOpPackageName());
}
java中普通的線程其他方法調用執行情況:
public class ThreadTest2 {
static class MyTask extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "_run");
}
void onPreviewFrame(){
System.out.println(Thread.currentThread().getName() + "_onPreviewFrame");
}
}
public static void main(String[] args) {
//子線程
MyTask task = new MyTask();
task.start();
//在主線程執行
task.onPreviewFrame();
}
}
4.IntentService
4.1.原理和使用
IntentService(本質:Service+HandlerThread+Intent)
要通過startService來啓動,bindService沒什麼用;
public class IntentServiceActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service);
}
//發送意圖給IntentService,啓動子線程執行任務
public void mClick(View btn){
Intent intent = new Intent(this,MyIntentService.class);
startService(intent);
}
}
public class MyIntentService extends IntentService {
//至少要有一個空的構造方法
public MyIntentService() {
super("MyIntentService");
}
public MyIntentService(String name) {
super(name);
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.d("jason", Thread.currentThread().getName() + "_onStart");
}
//UI線程發送Intent,會在子線程中執行
@Override
protected void onHandleIntent(Intent intent) {
Log.d("jason", Thread.currentThread().getName() + "_onHandleIntent");
}
}
至少要有一個空的構造方法
4.2.優點
- 1.提高子線程的優先級
- 2.減輕主線程的壓力
IntentService內部會創建一個HandlerThread,onHandleIntent在HandlerThread線程中執行
5.Loader
Activity中啓動子線程存在的問題:
- 1.內存泄露
- 2.無效的更新UI
Loader保證子線程與Activity或者Fragment的生命週期一致
Activity和Fragment自帶LoaderManager
5.1.優點:
1.方便
2.Activity或者Fragment的生命週期一致
3.數據緩存與更新通知
5.2.例子:
查詢通話記錄,是一個比較耗時的操作,應該放在子線程中處理。
優點:
1.數據查詢和跟新UI不需要自己去做線程切換和處理;
2.數據更新後,自動更新;
3.Activity銷燬後,自動取消查詢數據庫操作。
使用加載器加載通話記錄:
public class MainActivity extends Activity {
private static final String TAG = "jason";
// 查詢指定的條目
private static final String[] CALLLOG_PROJECTION = new String[] { CallLog.Calls._ID, CallLog.Calls.NUMBER,
CallLog.Calls.CACHED_NAME, CallLog.Calls.TYPE, CallLog.Calls.DATE };
private ListView mListView;
private MyLoaderCallback mLoaderCallback = new MyLoaderCallback();
private MyCursorAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.lv_list);
mAdapter = new MyCursorAdapter(MainActivity.this, null);
mListView.setAdapter(mAdapter);
//執行Loader的回調
getLoaderManager().initLoader(0, null, mLoaderCallback);
}
private class MyLoaderCallback implements LoaderManager.LoaderCallbacks<Cursor> {
//創建Loader
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
//加載的過程在子線程中進行
CursorLoader loader = new CursorLoader(MainActivity.this, CallLog.Calls.CONTENT_URI, CALLLOG_PROJECTION,
null, null, CallLog.Calls.DEFAULT_SORT_ORDER);
Log.d(TAG, "onCreateLoader");
return loader;
}
//Loader檢測底層數據,當檢測到改變時,自動執行新的載入獲取最新數據
//Activity/Fragment所需要做的就是初始化Loader,並且對任何反饋回來的數據進行響應。
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (data == null)
return;
mAdapter.swapCursor(data);
Log.d(TAG, "onLoadFinished data count = " + data.getCount());
}
//OnDestroy,自動停止load
@Override
public void onLoaderReset(Loader<Cursor> loader) {
Log.d(TAG, "onLoaderReset");
mAdapter.swapCursor(null);
}
}
}
作者:宋季航
鏈接:https://www.jianshu.com/p/15011b618c59
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。