线程优化

线程调度

线程调度原理

  • 任意时刻,只有一个线程占用CPU,处于运行状态
  • 多线程并发,轮流获取CPU使用权
  • JVM 负责线程的调度:按照特定的机制分配CPU使用权

调度模型

  • 分时调度模型:轮流获取,均分CPU时间
  • 抢占式调度模型:优先级高的进行获取,JVM采用

Android 线程调度

注意点

  • 线程避免过多,否则会导致CPU频繁切换,降低线程的运行效率
  • 通过任务重要性决定优先级是那种
  • 优先级还具有继承性,也就是UI线程中创建子线程,那么子线程的优先级与UI线程优先级一样

nice 值

Process中定义,值越小,优先级越高,默认是THREAD_PRIORITY_DEFAULT 0

不同的线程使用

Thread

最简单,常用的线程使用方式 - 不能够重复使用,频繁创建会造成很大的开销 - 在复杂的场景也不容易使用

HandlerThread

自带消息循环的线程 - 串行执行 - 长时间运行,不断从队列中获取任务

HandlerThread 是 Android 封装的一个线程类,将 Thread 跟 Handler 封装。使用步骤如下:

  • 创建 HandlerThread 实例对象 HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
  • 启动线程 mHandlerThread .start();

创建Handler对象,重写handleMessage方法

<br />    Handler mHandler= new Handler( mHandlerThread.getLooper() ) {
 
           @Override
 
           public boolean handleMessage(Message msg) {
 
               //消息处理
 
               return true;
 
           }
 
     });
 
 
  • 使用工作线程Handler向工作线程的消息队列发送消息:
<br />     Message message = Message.obtain();
 
     message.what = “2”
 
     message.obj = "骚风"
 
     mHandler.sendMessage(message);
 
 
  • 结束线程,即停止线程的消息循环 mHandlerThread.quit();是一个Android线程封装类,将Thread与handler结合使用

优势:

  1. 将loop运行在子线程中处理,减轻了主线程的压力,使主线程更流畅
  2. 串行执行,开启一个线程起到多个线程的作用
  3. 有自己的消息队列,不会干扰UI线程

劣势:

  1. 由于每一个任务队列逐步执行,一旦队列耗时过长,消息延时
  2. 对于IO等操作,线程等待,不能并发

IntentService

IntentService 是 Service 的子类,默认为我们开启了一个工作线程,使用这个工作线程逐一处理所有启动请求,在任务执行完毕后会自动停止服务,使用简单,只要实现一个方法 onHandleIntent,该方法会接收每个启动请求的 Intent,能够执行后台工作和耗时操作。可以启动 IntentService 多次,而每一个耗时操作会以队列的方式在 IntentService 的 onHandlerIntent 回调方法中执行,并且,每一次只会执行一个工作线程,执行完第一个再执行第二个。并且等待所有消息都执行完后才终止服务。

IntentService 适用于 APP 在不影响当前用户的操作的前提下,在后台默默的做一些操作。

IntentService源码:

  • 通过 HandlerThread 单独开启一个名为 IntentService 的线程
  • 创建一个名叫 ServiceHandler 的内部 Handler
  • 把内部Handler与HandlerThread所对应的子线程进行绑定
  • 通过 onStartCommand() 传递给服务 intent,依次插入到工作队列中,并逐个发送给 onHandleIntent()
  • 通过 onHandleIntent() 来依次处理所有 Intent 请求对象所对应的任务
  • 如果start了多次,每一次都会在WorkerThread中依次执行,当全部执行完成,它就会自己调用stopSelf()结束自己。

使用示例:

<br />public class MyIntentService extends IntentService {
 
    public static final String TAG ="MyIntentService";
 
    public MyIntentService() {
 
        super("MyIntentService");
 
    }
 
 
 
    @Override
 
    protected void onHandleIntent(@Nullable Intent intent) {
 
 
 
       boolean isMainThread = Thread.currentThread() == Looper.getMainLooper().getThread();
 
        Log.i(TAG,"is main thread:"+isMainThread); // 这里会打印false,说明不是主线程
 
 
 
        // 模拟耗时操作
 
        download();
 
    }
 
 
 
    /**
 
     * 模拟执行下载
 
     */
 
    private void download(){
 
       try {
 
           Thread.sleep(5000);
 
           Log.i(TAG,"下载完成...");
 
       }catch (Exception e){
 
           e.printStackTrace();
 
       }
 
    }
 
}
 
 

AsyncTask

Android 提供工具类,无需自己处理线程切换 使用及源码详解见 https://www.ztzyif.top/index.php/2019/07/16/asynctask-%e6%ba%90%e7%a0%81%e8%a7%a3%e6%9e%90/

线程池

Java提供的线程池 - 容易复用,减少频繁创建及销毁时间 - 功能强大:定时、任务队列,并发控制

RxJava

由强大的Scheduler集合组成 源码详解见 https://www.ztzyif.top/index.php/2019/07/17/rxjava2-%e8%a7%a3%e6%9e%90/

线程使用准则

创建线程必须命名 - 方便进行定位线程归属 - 运行期间调用Thread.currentThread.setName()修改名字

重视优先级设置 Process.setThreadPriority()

public class ThreadPoolUtils {
 
    private int CPUCOUNT = Runtime.getRuntime().availableProcessors();
 
    private ThreadPoolExecutor cpuExecutor = new ThreadPoolExecutor(CPUCOUNT, CPUCOUNT,
            30, TimeUnit.SECONDS, new LinkedBlockingDeque&lt;Runnable&gt;(), sThreadFactory);
 
    private ThreadPoolExecutor iOExecutor = new ThreadPoolExecutor(64, 64,
            30, TimeUnit.SECONDS, new LinkedBlockingDeque&lt;Runnable&gt;(), sThreadFactory);
 
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
 
//对每个线程进行命名,原子性的自增       
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "ThreadPoolUtils #" + mCount.getAndIncrement());
        }
    };
 
    public static ExecutorService getService() {
        return sService;
    }
 
    private static ExecutorService sService = Executors.newFixedThreadPool(5, new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, "ThreadPoolUtils");
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            return thread;
        }
    });
}
 
// 以下代码是为了演示修改任务的名称
        ThreadPoolUtils.getService().execute(new Runnable() {
            @Override
            public void run() {
                Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                String oldName = Thread.currentThread().getName();
                Thread.currentThread().setName("new Name");
                LogUtils.i("");
                Thread.currentThread().setName(oldName);
            }
        });
 

统一线程库

  1. 区分任务类型:IO、CPU密集型
  2. IO密集型任务不消耗CPU,核心池可以很大
  3. CPU密集型:核心池与CPU核数有关

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java 锁分类

    乐观锁是一种乐观思想,认为读多写少,遇到并发的可能性低,每次拿数据时候并不会上锁,因为认为不会被别人修改。但是更新的时候会判断有没有人会更新这条数据,采取写的时...

    Yif
  • Android 开发艺术探索笔记二

    不管是Activity,Dialog还是Toast,它们视图都是附加在window上的,window才是view的直接管理者。

    Yif
  • App 启动优化

    adb shell start am -W packname/首屏activity

    Yif
  • 手把手教你看懂线程池源码!

    使用线程池,一般会使用JDK提供的几种封装类型,即:newFixedThreadPool、newSingleThreadExecutor、newCachedTh...

    业余草
  • 带你用生活大白话理解 NIO

    今晚是个下雨天,写完今天最后一行代码,小强起身合上电脑,用滚烫的开水为自己泡制了一桶老坛酸菜牛肉面。这大概是苦逼程序猿给接下来继续奋战的自己最好的馈赠。年轻的程...

    程序员小强
  • 干货:Java多线程详解(内附源码)

    技术zhai
  • Java的ThreadPoolExecutor

    Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程。在底层,操作系统内核将这些线程映射到...

    用户3467126
  • 想要金九银十面试通关,不懂 Java多线程肯定是不行的!

    如果对什么是线程、什么是进程仍存有疑惑,请先 Google 之,因为这两个概念不在本文的范围之内。

    程序员追风
  • Java基础--线程创建方式

    线程的创建主要有两种形式,通过继承Thread或者实现Runnable接口,本质上没有太大区别。

    河岸飞流
  • 线程池的初步了解

    运行一下,我这里第一段代码使用了线程池的时间是194ms,第二段代码不使用线程池的时间是2043ms。这里默认的线程池中的线程数是100,如果把这个数量减小,虽...

    技术从心

扫码关注云+社区

领取腾讯云代金券