android 多线程

  • 线程与进程的区别
    一个进程属于一个独立的运行空间,可以理解为一个程序或者一个应用,而线程属于进程中的一个执行任务,线程是进程的一个子集,维度不同,一个进程可以有多个线程,每个线程处理的任务可以都不同,不同的进程有不同的内存空间,而一个进程中的所有线程是共享同一片内存空间的.每个线程又有自己单独的栈内存,用来储存本地数据.

  • 多线程的有点
    多个线程同时处理任务会提高程序的运行效率,多个线程是共享堆内存,因此创建多个线程去处理一批任务比创建多个进程处理任务要好,效率更加高.

  • 线程的优先级
    每个线程都是有优先级的,一般情况优先级高的线程是有优先执行权,这是依赖系统的调度实现的,我们是可以定义线程的优先级别的 (1-10),但这并不能保证优先级高的线程会优先执行.

  • 启动线程的run方法与start方法
    启动一个线程是用start方法,但是这并不代表该线程立即运行,只是说该线程所处的虚拟处理机处于可运行状态,可以有jvm来调度执行,run方法是线程启动后要回调的方法.

  • sleep 方法与wait方法区别
    sleep 方法会将线程休眠(暂停),是Thread的静态方法,调用此方法会将线程暂停指定时间,将CPU资源让给其他线程,但是不会释放对象锁,其线程暂停过程是一直持有对象锁的.
    wait方法是Object的静态方法,此方法会让该线程释放对象锁,进入对象的等待池,调用notify或者notifyAll方法才能唤醒等待池中的线程,进入等锁池,当该线程拿到锁后就可以进入就绪状态.

  • sleep方法与yield方法
    sleep方法给其他线程运行机会的时候不会考虑其他线程的优先级,但是yield方法只会给相同级别或更高级别的线程运行机会.
    sleep 方法会是线程进入阻塞状态,而yield方法会让线程转入就绪状态
    sleep方法会抛出阻断异常,而yield没有任何异常

  • 死锁(Deadlock)
    死锁是指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。
    分析死锁,我们需要查看Java应用程序的线程转储。我们需要找出那些状态为BLOCKED的线程和他们等待的资源。每个资源都有一个唯一的id,用这个id我们可以找出哪些线程已经拥有了它的对象锁。
    避免嵌套锁,只在需要的地方使用锁和避免无限期等待是避免死锁的通常办法

  • Android中使用多线程

    1. 方法一:继承Thread类,重写父类的run方法,用start方法启动.
    2. 方法二:实现runnable方法

    在子线程处理任务后如何更新主线程UI,Google 规定所有更新UI元素的动作必须要在主线程,但是主线程又不能进行耗时工作,所以对应出来一套异步消息机制.Handler
    Message:可以在线程之间传递消息,进行数据交换.
    Handler:消息的发送与处理
    MessageQueue:存放所有message消息,直到被处理,每一个线程只有有个messagequeue.
    Looper:用来管理Messagequeue的队列,Looper.loop()后就会进入无限循环中,将messagequeue中的消息取出来给handler,每个线程只有一个Looper

  • AsyncTask

        public abstract class AsyncTask<Params, Progress, Result> {
    
        private static final String LOG_TAG = "AsyncTask";
    
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        //核心线程数
        private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
        //最大线程数
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        //非核心线程等待时间
        private static final int KEEP_ALIVE = 1;
        private static final ThreadFactory sThreadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);
    
            public Thread newThread(Runnable r) {
                return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
            }
        };
        //线程队列 128
        private static final BlockingQueue<Runnable> sPoolWorkQueue =
                new LinkedBlockingQueue<Runnable>(128);
    
        /**
         * An {@link Executor} that can be used to execute tasks in parallel.
         */
        public static final Executor THREAD_POOL_EXECUTOR
                = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                        TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
        ....
        ....
        }
    

    为了更好的在子线程中更新主线程UI,Android基于异步消息机制封装的一个工具类,其原理是:
    两个线程池 + handler
    任务队列线程池:任务调度,让多个线程按序排列
    执行线程池:真正具体执行任务的线程池
    handler:主线程与子线程消息通讯

    具体api:
    execute:在主线程中执行,触发异步任务.
    onPreExecute:执行任务前的准备动作,比如初始化进度条,主线程
    doInbakground:子线程执行任务,不可以改变UI ,运行在子线程
    onProgressUpdate: 更新进度,在主线程
    onPostExectue: 子线程结束时候自动调用该方法,表示线程任务处理完毕
    onCancelled:异步任务可取消状态

  • Android线程池
    传统的使用线程的方法是直接new Thread创建一个子线程,这样会出现一个严重的问题:在众多任务的时候,系统会为每一个任务创建一个线程,任务执行完毕后又会销毁该线程,这样频繁的创建与销毁线程会消耗大量资源,造成系统卡顿,特别是销毁线程的时候会频繁的调用垃圾回收机制,性能低下且耗时.所以用线程池统一管理线程,对线程进行重复使用,在任务完成后并不是立即销毁线程,而是等待另外的任务.

    几种常见的线程池:

    1. ThreadPoolExecutor:基本线程池,
    public ThreadPoolExecutor(int corePoolSize,
                                int maximumPoolSize,
                                long keepAliveTime,
                                TimeUnit unit,
                                BlockingQueue<Runnable> workQueue,
                                ThreadFactory threadFactory) {
          ...
    }
    

    corePoolSize: 该线程池中核心线程的数量。
    maximumPoolSize:该线程池中最大线程数量。(区别于corePoolSize)
    keepAliveTime:从字面上就可以理解,是非核心线程空闲时要等待下一个任务到来的时间,当任务很多,每个任务执行时间很短的情况下调大该值有助于提高线程利用率。注意:当allowCoreThreadTimeOut属性设为true时,该属性也可用于核心线程。
    unit:上面时间属性的单位
    workQueue:任务队列,后面详述。
    threadFactory:线程工厂,可用于设置线程名字等等,一般无须设置该参数。

    1. 可重用固定线程数 FixedThreadPool
      创建一个线程数固定的线程池
      final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

    创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不再添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)

    适用:执行长期的任务,性能好很多

    1. 按需创建 CachedThreadPool
      没有核心线程,只有非核心线程,并且每个非核心线程空闲等待的时间为60s,采用SynchronousQueue队列

      final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

      当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。

      适用:执行很多短期异步的小程序或者负载较轻的服务器

    2. 单个核线的 SingleThreadPool
      只有一个核心执行线程,当该线程被占用的时候,其他任务需要进入等待队列

      创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)
      适用:一个任务一个任务执行的场景

    3. 定时延时执行 ScheduledThreadPool
      final ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);//参数是核心数

      创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构

      适用:周期性执行任务的场景

    4. 队列中有优先级比较的线程池 PriorityThreadPool
      final ExecutorService priorityThreadPool = new ThreadPoolExecutor(3,3,0, TimeUnit.SECONDS,new PriorityBlockingQueue());

      通过线程优先级来执行任务

  • 7种常见的阻塞队列
    ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
    LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
    PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
    DelayQueue:一个使用优先级队列实现的无界阻塞队列。
    SynchronousQueue:一个不存储元素的阻塞队列。
    LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
    LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

  • 线程池常用方法
    1.shutDown() 关闭线程池,不影响已经提交的任务

    2.shutDownNow() 关闭线程池,并尝试去终止正在执行的线程

    3.allowCoreThreadTimeOut(boolean value) 允许核心线程闲置超时时被回收

    4.submit 一般情况下我们使用execute来提交任务,但是有时候可能也会用到submit,使用submit的好处是submit有返回值。

    5.beforeExecute() - 任务执行前执行的方法

    6.afterExecute() -任务执行结束后执行的方法

    7.terminated() -线程池关闭后执行的方法

posted @ 2023-02-07 10:44  年年糕  阅读(39)  评论(0编辑  收藏  举报