多线程

2017-11-10  本文已影响0人  badcyc

多线程


常见的耗时的操作如下:

对耗时任务其实并没有一个清晰固定的定义,但是只要应用中出现了卡顿的情况,比如按钮反应慢,动画卡壳,这些都算是
耗时任务。特别是,在UI线程中对动画效果的计算比点击按钮更敏感

运行环境

一个Android应用中的线程使用java.lang.Thread来表示。它是Android中最基础的运行环境。一个线程存活的时间取决于任务
的长短。线程支持实现了java.lang.Runnable接口的任务的执行。

private class MyTask implements Runnable{ 
     public void run(){ 
     int i = 0; // Stored on the thread local task 
     } 
  }

run()方法中的所有本地变量都将会被存储在这个线程的内存栈中。当初始化和开始了一个线程之后,这个任务就开始执行
了。
Thread myThread = new Thread(new MyTask()); myThread.start();
在操作系统层面,一个线程有一个指令指针和一个栈指针。指令指针指向下一条要执行的指令,栈指针指向了一块私有的内存区域——对其他线程是不可用的——这里存储了线程的本地数据。线程本地数据从代码上来看,其实就是定义在run方法里的变量。

CPU在某一刻只能执行从一个线程中执行代码,但是一个系统通常有多个要求同时执行的线程,比如一个同时运行多个应用的系统。从用户的感受来说,应用可以并行执行,CPU必须将他的处理时间在多个应用线程之间进行分享。调度器用来处理对CPU执行时间的分配。调度策略可以有多种实现方式,但是它主要是基于线程优先级:具有较高优先级的线程会先于较低
优先级的线程获得CPU。在java中,线程的优先级被设置为1——10,但是,如果不明确设定优先级,默认的优先级是5

myThread.setPriority(8);

然而,如果调度过程只依赖于优先级的高低,那么优先级较低的线程可能无法获得足够的运行时间来完成任务。因此,调度器也应该在改变执行线程的时候将每个线程的运行时间考虑在内。运行线程的切换被称为contextswitch(上下文切换)。当运
行中的线程的状态被存储完成后,context switch开始,这使得随后可以恢复这个线程的状态,然后,这个线程进入wait(等
待/挂起)状态。接下来调度器将会恢复一个等待中的线程开始执行。

单线程应用

每个应用至少有一个线程。如果没有其他的线程,那么所有的代码都会运行在同一个执行序列中,每条指令都必须等待在它之前的所有指令都运行完成后,它自己才能运行。
优点:

缺点:

解决方案:

多线程应用

多线程的方式

2839011-23a52dcb6f73550f.png

Android提供常见的操作多线程的方式

1. Handler+Thread
2. AsyncTask
3. Rxjava

各个方式的优缺点:

handler sendMessage用法
图解

2839011-7036df5ffea97ec2.png

优缺点:

1. Handler用法简单明了,可以将多个异步任务更新UI的代码放在一起,清晰明了
2. 处理单个异步任务代码略显多

适用范围:

 多个异步任务的更新UI

AsyncTask
优缺点:

1. 处理单个异步任务简单,可以获取到异步任务的进度
2. 可以通过cancel方法取消还没执行完的AsyncTask
3. 处理多个异步任务代码显得较多

适用范围:

单个异步任务的处理

ThreadPoolExecutor
Executors提供了四种创建ExecutorService的方法,他们的使用场景如下:

1. Executors.newCachedThreadPool()
   创建一个定长的线程池,每提交一个任务就创建一个线程,直到达到池的最大长度,这时线程池会保持长度不再变化
2. Executors.newFixedThreadPool()
   创建一个可缓存的线程池,如果当前线程池的长度超过了处理的需要时,它可以灵活的回收空闲的线程,当需要增加时,
    它可以灵活的添加新的线程,而不会对池的长度作任何限制
3. Executors.newScheduledThreadPool()
   创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer
4. Executors.newSingleThreadExecutor()
   创建一个单线程化的executor,它只创建唯一的worker线程来执行任务

适用范围

 批处理任务

Rxjava

1. Rx的全称:

Reactive Extensions(响应式扩展)。Rx基于观察者模式,目的是提供一致的编程接口,帮助开发者更方便的处理异步数据流。ReactiveX结合了观察者模式、迭代器模式和函数式编程的精华。而Rxjava是针对java语言的一个异步的响应式编程库。

2. 怎么使用Rxjava


1863579-b959455ac1f04ec3.png

基础:

                  Observable<String> observable=Observable.create(new  Observable.OnSubscribe<String>() {
                @Override
                public void call(Subscriber<? super String> subscriber) {
                    subscriber.onNext("Hello World");
                    subscriber.onCompleted();
                }
            });
            Subscriber<String> subscriber=new Subscriber<String>() {
                @Override
                public void onCompleted() {
       }
       @Override
                public void onError(Throwable e) {
    
                }
    
                @Override
                public void onNext(String s) {
                    System.out.print(s);
                }
            };
            observable.subscribe(subscriber);
        }

lambda可以使代码变得简洁: > ```Observable.just("Hello World")

            .subscribe(s->System.out.println(s));
            `
1863579-433c53337adbaf31.png

操作符(operators)

Observable.just("Hello, world!")  
  .map(new Func1<String, String>() {  //string->string
      @Override  
      public String call(String s) {  
          return s + " -Dan";  
      }  
  })  
  .subscribe(s -> System.out.println(s));  
  
  Observable.just("Hello World")
                .map(s -> s.hashCode())         //string -> integer
                .subscribe(s->System.out.println(Integer.toString(s)));

操作符

假如有这样一个方法:
根据输入的字符返回一个网站的url列表类似于搜索引擎

public static Observable<List<String>> query(String s){
        List<String> urls=new ArrayList<>();
        urls.add("www");
        urls.add("http");
        urls.add("https");
        Observable<List<String>> observable=Observable.create(new Observable.OnSubscribe<List<String>>() {
            @Override
            public void call(Subscriber<? super List<String>> subscriber) {
                subscriber.onNext(urls);
            }
        });
        return observable;
    }
query(u).subscribe(urls-> {
            for (String url:urls)
            System.out.println(url);
        });
        query(u).subscribe(urls->{
        Observable.from(urls)
                .subscribe(url->System.out.println(url));
      });

Observable.from()方法他接收一个集合作为输入,然后每次输出一个元素给subscriber;(Observable.from也是Observable(被观察者对象))

query(u).subscribe(urls->{
        Observable.from(urls)
                .subscribe(url->System.out.println(url));
      });/*虽然去掉了for each循环,但是代码依然看起来很乱。多个嵌套的subscription不仅看起来很丑,难以修改,更严重的是它会破坏某些RxJava的特性。*/

改进——flatMap()
Observable.flatMap()接收一个Observable的输出作为输入,输出另一个Observable。

query("Hello World")
                .flatMap(new Func1<List<String>, Observable<String>>() {
                    @Override
                    public Observable<String> call(List<String> list) {
                        return  Observable.from(list);
                    }
                }).subscribe(url->System.out.println(url));

lambda简化代码

query("s")
                .flatMap(urls->Observable.from(urls))
                .subscribe(url->System.out.println(url));
flatMap()也可以返回任何它想返回的对象

丰富的操作符

query("Hello, world!")  
    .flatMap(urls -> Observable.from(urls))  
    .flatMap(url -> getTitle(url))  
    .filter(title -> title != null)  
    .take(5)  
    .doOnNext(title -> saveTitle(title))  
    .subscribe(title -> System.out.println(title)); 

上述示例的功能过滤掉返回的title列表种null的值
输出最多5个结果
在println之前把每个标题保存到磁盘上

错误处理

 Observable.just("Hello, world!")
    .map(s -> potentialException(s))
    .map(s -> anotherPotentialException(s))
    .subscribe(new Subscriber<String>() {
        @Override
        public void onNext(String s) { System.out.println(s); }
        @Override
        public void onCompleted() { System.out.println("Completed!"); }
         @Override
        public void onError(Throwable e) { System.out.println("error!"); }
    });

代码中的potentialException() 和 anotherPotentialException()有可能会抛出异常。每一个Observerable对象在终结的时候都会调用onCompleted()或者onError()方法,所以Demo中会打印”Completed!”或者"error!”。

调度器

多线程的实现——subscribeOn()指定观察者代码运行的线程,使用observerOn()指定订阅者运行的线程:

 myObservableServices.retrieveImage(url)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
1863579-9b47a29c4e93f43b.png

多种线程:

• Schedulers.immediate,默认,当前线程,立即执行

• Schedulers.trampoline,当前线程,要等其他任务先执行

• Schedulers.newThread,启用一个新线程

• Schedulers.io,擅长读写文件、数据库、网络信息,一个无数量上限的线程池

• Schedulers.computation,擅长cpu密集型计算,固定线程池,不要做io操作,会浪费cpu

• AndroidSchedulers.mainThread,Android主线程

相比其他多线程的方式,rxjava只需要添加两个操作符其他代码保持不变就可以实现多线程。而AsyncTask等需要找出需要并发的部分

灵活地切换线程
1863579-ab448f99561e79c5.png

•subscribeOn只能定义一次,除非是在定义doOnSubscribe
•observeOn可以定义多次,决定后续代码所在的线程

订阅(subscriptions)

Observable.subscribe(),会返回一个SubScription对象,代表了被观察者和订阅者之间的联系。
在后面可以通过调用unsubscribe()取消订阅,从而达到避免内存泄露的目的

Subscription s = query("s")
                .flatMap(urls -> Observable.from(urls))
                .subscribe(url -> System.out.println(url));
//ctrl+Alt+V快速得到返回对象
s.unsubscribe();//取消订阅

Android中使用Rxjava——RxAndroid

AndroidSchedulers提供了针对Android的线程系统的调度器

在UI线程执行某些代码只需要调用AndroidSchedulers.mainThread();

上一篇 下一篇

猜你喜欢

热点阅读