美文网首页
多线程优化

多线程优化

作者: 贾里 | 来源:发表于2019-02-13 19:44 被阅读11次

    1.线程通信基础

    • 生成者消费者

    2.AsyncTask

    • FutureTask
    • 线程池
    • 问题和缺点

    3.HandlerThread

    • 优点
    • 例子
    1. 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.haomeiwen.com/subject/mtzyfftx.html