导航:首页 > 操作系统 > android线程实现方式

android线程实现方式

发布时间:2023-06-16 19:37:09

1. android创建子线程

创建后台线程的方法有多种,这里说三种,可以回去试试

1、使用Android系统工具类 AsyncTask(Params,Progress,Result)
AsyncTask是一个轻量级线程,三个泛型参数分别是 Params传入参数,int型Progress为进度条进度,Result为返回值
要使用AsyncTask,必须继承之并复写其中的几个重要的函数。
onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

注:Task必须在UI线程中创建,并调用并且只能调用一次execute方法,该方法的参数为传入的泛型Params。
其余函数最好不要手动调用,容易造成线程崩溃。多次调用Task,容易造成线程池溢出。

2、使用Handler和HandlerThread
误区: Handler handler = new Handler ();
handler.post(r);
这种做法看似创建了一个线程,但实际上handler只是直接调用Runnable中的run() 方法,而不执行线程的start()函数,所以这两句代码执行后,程序仍然在UI线程中执行。所以我们引入HandlerThread,因为HandlerThread中有一个Looper对象,用以循环消息队列。
为了使用Looper,必须子类化Handler,并复写它的构造函数。
class MyHandler extends Handler{
public MyHandler() {}
public MyHandler(Looper looper){
super (looper);
}

public void handleMessage(Message msg){
//....这里运行耗时的过程
}
}
}
handleMessage(Message msg)函数用以接收消息,msg则是从UI线程中发出的消息,可以传递各种对象,根据这些对象和数值进行操作。
有了Handler子类,则可以在UI线程中进行创建和初始化
HandlerThread handlerThread = new HandlerThread( "backgroundThread" );
handlerThread.start();
MyHandler myHandler = new MyHandler(handlerThread.getLooper());
Message msg = myHandler.obtainMessage();
//....此处对msg进行赋值,可以创建一个Bundle进行承载
msg.sendToTarget();
之后如果需要调用线程,只要对handler传入msg,就可以执行相应的过程了
最后,很重要的一点,HandlerThread 不随Activity的生命周期结束而消亡,所以必须复写Ondestory(),调用HandlerThread .stop()

3、使用线程同步 synchronized、 wait()、 notify()
使用线程同步机制synchronized实现多线程操作,相对来说比较复杂,但也是灵活性最佳、效率最高的一种做法,在产品开发当中也使用得最广。本人水平相当有限,也只学得一点皮毛。
synchronized相当于一把锁,当线程运行的时候,会同时有几个线程访问一个对象,对其进行操作或者修改。这可能引起很不良的后果(例如改变判定条件,或者在别的线程还没使用完的时候对象已经被析构等等),所以必须对一些对象进行加锁,保证它在同一时间只允许被一个线程访问。
synchronized使用方法有两种:
<1> 同步方法在方法名前加入synchronized关键字,则该方法在同一时间内只允许一个线程访问它,其代码逻辑较为简单,但使用起来不太灵活,并且大大降低效率,耗时太长的同步方法甚至会使得多线程失去原本的意义成为单线程
<2>同步参数 对某一个代码块加锁,并且以synchronized(obj)的方式,表明该代码块内的obj对象是线程同步的。这个做法使得代码灵活性大大加强,缩小同步代码块的范围,并且可以在不同的函数体和类内进行同步,唯一遗憾的是实现起来比较复杂,容易产生一些问题

而wait()和notify(),必须在synchronized锁的代码块内执行,否则系统将会报错。
有了以上基础,就可以很容易的创建后台线程了

Private Runnable backgroundRunnable = new Runnable () {
@Override
public void run() {
if(isFinish){
//.....
break;
}
for(;;){
synchronized(this){
//....写耗时过程
wait();
}
}
}
}

UI线程中,就是一般的创建线程过程了
Thread backgroundThread = new Thread (backgroundRunnable);
backgroundThread.start();
这样,在后台线程中会不断的循环,当执行完一次过程以后就会wait()休眠。然后在OnTouchListener或者OnClickListener内相应的位置执行
synchronized(backgroundRunnable){
backgroundRunnable.notify();
}
当用户触摸屏幕产生一个event的时候,线程就会被唤醒,执行下一次循环。
最后,还是内存安全问题,必须复写Activity中的OnDestory()方法,将标志量isFinish设为false,并且backgroundThread .stop()

2. Android进程间和线程间通信方式

        进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

  线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一些在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

  区别:

  (1)、一个程序至少有一个进程,一个进程至少有一个线程;

  (2)、线程的划分尺度小于进程,使得多线程程序的并发性高;

  (3)、进程在执行过程中拥有独立的内存单元,而多个线程共享内存,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。

---------------------

一、Android进程间通信方式

1.Bundle

  由于Activity,Service,Receiver都是可以通过Intent来携带Bundle传输数据的,所以我们可以在一个进程中通过Intent将携带数据的Bundle发送到另一个进程的组件。

  缺点:无法传输Bundle不支持的数据类型。

2.ContentProvider

  ContentProvider是Android四大组件之一,以表格的方式来储存数据,提供给外界,即Content Provider可以跨进程访问其他应用程序中的数据。用法是继承ContentProvider,实现onCreate,query,update,insert,delete和getType方法,onCreate是负责创建时做一些初始化的工作,增删查改的方法就是对数据的查询和修改,getType是返回一个String,表示Uri请求的类型。注册完后就可以使用ContentResolver去请求指定的Uri。

3.文件

  两个进程可以到同一个文件去交换数据,我们不仅可以保存文本文件,还可以将对象持久化到文件,从另一个文件恢复。要注意的是,当并发读/写时可能会出现并发的问题。

4.Broadcast

  Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播。

5.AIDL方式

  Service和Content Provider类似,也可以访问其他应用程序中的数据,Content Provider返回的是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。

         AIDL通过定义服务端暴露的接口,以提供给客户端来调用,AIDL使服务器可以并行处理,而Messenger封装了AIDL之后只能串行运行,所以Messenger一般用作消息传递。

6.Messenger

  Messenger是基于AIDL实现的,服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。

  双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。

7.Socket

  Socket方法是通过网络来进行数据交换,注意的是要在子线程请求,不然会堵塞主线程。客户端和服务端建立连接之后即可不断传输数据,比较适合实时的数据传输

二、Android线程间通信方式

  一般说线程间通信主要是指主线程(也叫UI线程)和子线程之间的通信,主要有以下两种方式:

1.AsyncTask机制

  AsyncTask,异步任务,也就是说在UI线程运行的时候,可以在后台的执行一些异步的操作;AsyncTask可以很容易且正确地使用UI线程,AsyncTask允许进行后台操作,并在不显示使用工作线程或Handler机制的情况下,将结果反馈给UI线程。但是AsyncTask只能用于短时间的操作(最多几秒就应该结束的操作),如果需要长时间运行在后台,就不适合使用AsyncTask了,只能去使用Java提供的其他API来实现。

2.Handler机制

  Handler,继承自Object类,用来发送和处理Message对象或Runnable对象;Handler在创建时会与当前所在的线程的Looper对象相关联(如果当前线程的Looper为空或不存在,则会抛出异常,此时需要在线程中主动调用Looper.prepare()来创建一个Looper对象)。使用Handler的主要作用就是在后面的过程中发送和处理Message对象和让其他的线程完成某一个动作(如在工作线程中通过Handler对象发送一个Message对象,让UI线程进行UI的更新,然后UI线程就会在MessageQueue中得到这个Message对象(取出Message对象是由其相关联的Looper对象完成的),并作出相应的响应)。

三、Android两个子线程之间通信

  面试的过程中,有些面试官可能会问Android子线程之间的通信方式,由于绝大部分程序员主要关注的是Android主线程和子线程之间的通信,所以这个问题很容易让人懵逼。

  主线程和子线程之间的通信可以通过主线程中的handler把子线程中的message发给主线程中的looper,或者,主线程中的handler通过post向looper中发送一个runnable。但looper默认存在于main线程中,子线程中没有Looper,该怎么办呢?其实原理很简单,把looper绑定到子线程中,并且创建一个handler。在另一个线程中通过这个handler发送消息,就可以实现子线程之间的通信了。

  子线程创建handler的两种方式:

  方式一:给子线程创建Looper对象:

new Thread(new Runnable() {

            public void run() { 

                Looper.prepare();  // 给这个Thread创建Looper对象,一个Thead只有一个Looper对象

                Handler handler = new Handler(){ 

                    @Override 

                    public void handleMessage(Message msg) { 

                        Toast.makeText(getApplicationContext(), "handleMessage", Toast.LENGTH_LONG).show(); 

                    } 

                }; 

                handler.sendEmptyMessage(1); 

                Looper.loop(); // 不断遍历MessageQueue中是否有消息

            }; 

        }).start();

---------------------

       方式二:获取主线程的looper,或者说是UI线程的looper:

new Thread(new Runnable() {

            public void run() { 

                Handler handler = new Handler(Looper.getMainLooper()){ // 区别在这!!! 

                    @Override 

                    public void handleMessage(Message msg) { 

                        Toast.makeText(getApplicationContext(), "handleMessage", Toast.LENGTH_LONG).show(); 

                    } 

                }; 

                handler.sendEmptyMessage(1); 

            }; 

        }).start();

---------------------

3. Android 中的“子线程”解析

Android 中线程可分为 主线程 和 子线程 两类,其中主线程也就是 UI线程 ,它的主要这作用就是运行四大组件、处理界面交互。子线程则主要是处理耗时任务,也是我们要重点分析的。

首先 Java 中的各种线程在 Android 里是通用的,Android 特有的线程形态也是基于 Java 的实现的,所以有必要先简单的了解下 Java 中的线程,本文主要包括以下内容:

在 Java 中要创建子线程可以直接继承 Thread 类,重写 run() 方法:

或者实现 Runnable 接口,然后用Thread执行Runnable,这种方式比较常用:

简单的总结下:

Callable 和 Runnable 类似,都可以用来处理具体的耗时任务逻辑的,但是但具体的差别在哪里呢?看一个小例子:

定义 MyCallable 实现了 Callable 接口,和之前 Runnable 的 run() 方法对比下, call() 方法是有返回值的哦,泛型就是返回值的类型:

一般会通过线程池来执行 Callable (线程池相关内容后边会讲到),执行结果就是一个 Future 对象:

可以看到,通过线程池执行 MyCallable 对象返回了一个 Future 对象,取出执行结果。

Future 是一个接口,从其内部的方法可以看出它提供了取消任务(有坑!!!)、判断任务是否完成、获取任务结果的功能:

Future 接口有一个 FutureTask 实现类,同时 FutureTask 也实现了 Runnable 接口,并提供了两个构造函数:

用 FutureTask 一个参数的构造函数来改造下上边的例子:

FutureTask 内部有一个 done() 方法,代表 Callable 中的任务已经结束,可以用来获取执行结果:

所以 Future + Callable 的组合可以更方便的获取子线程任务的执行结果,更好的控制任务的执行,主要的用法先说这么多了,其实 AsyncTask 内部也是类似的实现!

注意, Future 并不能取消掉运行中的任务,这点在后边的 AsyncTask 解析中有提到。

Java 中线程池的具体的实现类是 ThreadPoolExecutor ,继承了 Executor 接口,这些线程池在 Android 中也是通用的。使用线程池的好处:

常用的构造函数如下:

一个常规线程池可以按照如下方式来实现:

执行任务:

基于 ThreadPoolExecutor ,系统扩展了几类具有新特性的线程池:

线程池可以通过 execute() 、 submit() 方法开始执行任务,主要差别从方法的声明就可以看出,由于 submit() 有返回值,可以方便得到任务的执行结果:

要关闭线程池可以使用如下方法:

IntentService 是 Android 中一种特殊的 Service,可用于执行后台耗时任务,任务结束时会自动停止,由于属于系统的四大组件之一,相比一般线程具有较高的优先级,不容易被杀死。用法和普通 Service 基本一致,只需要在 onHandleIntent() 中处理耗时任务即可:

至于 HandlerThread,它是 IntentService 内部实现的重要部分,细节内容会在 IntentService 源码中说到。

IntentService 首次创建被启动的时候其生命周期方法 onCreate() 会先被调用,所以我们从这个方法开始分析:

这里出现了 HandlerThread 和 ServiceHandler 两个类,先搞明白它们的作用,以便后续的分析。

首先看 HandlerThread 的核心实现:

首先它继承了 Thread 类,可以当做子线程来使用,并在 run() 方法中创建了一个消息循环系统、开启消息循环。

ServiceHandler 是 IntentService 的内部类,继承了 Handler,具体内容后续分析:

现在回过头来看 onCreate() 方法主要是一些初始化的操作, 首先创建了一个 thread 对象,并启动线程,然后用其内部的 Looper 对象 创建一个 mServiceHandler 对象,将子线程的 Looper 和 ServiceHandler 建立了绑定关系,这样就可以使用 mServiceHandler 将消息发送到子线程去处理了。

生命周期方法 onStartCommand() 方法会在 IntentService 每次被启动时调用,一般会这里处理启动 IntentService 传递 Intent 解析携带的数据:

又调用了 start() 方法:

就是用 mServiceHandler 发送了一条包含 startId 和 intent 的消息,消息的发送还是在主线程进行的,接下来消息的接收、处理就是在子线程进行的:

当接收到消息时,通过 onHandleIntent() 方法在子线程处理 intent 对象, onHandleIntent() 方法执行结束后,通过 stopSelf(msg.arg1) 等待所有消息处理完毕后终止服务。

为什么消息的处理是在子线程呢?这里涉及到 Handler 的内部消息机制,简单的说,因为 ServiceHandler 使用的 Looper 对象就是在 HandlerThread 这个子线程类里创建的,并通过 Looper.loop() 开启消息循环,不断从消息队列(单链表)中取出消息,并执行,截取 loop() 的部分源码:

dispatchMessage() 方法间接会调用 handleMessage() 方法,所以最终 onHandleIntent() 就在子线程中划线执行了,即 HandlerThread 的 run() 方法。

这就是 IntentService 实现的核心,通过 HandlerThread + Hanlder 把启动 IntentService 的 Intent 从主线程切换到子线程,实现让 Service 可以处理耗时任务的功能!

AsyncTask 是 Android 中轻量级的异步任务抽象类,它的内部主要由线程池以及 Handler 实现,在线程池中执行耗时任务并把结果通过 Handler 机制中转到主线程以实现UI操作。典型的用法如下:

从 Android3.0 开始,AsyncTask 默认是串行执行的:

如果需要并行执行可以这么做:

AsyncTask 的源码不多,还是比较容易理解的。根据上边的用法,可以从 execute() 方法开始我们的分析:

看到 @MainThread 注解了吗?所以 execute() 方法需要在主线程执行哦!

进而又调用了 executeOnExecutor() :

可以看到,当任务正在执行或者已经完成,如果又被执行会抛出异常!回调方法 onPreExecute() 最先被执行了。

传入的 sDefaultExecutor 参数,是一个自定义的串行线程池对象,所有任务在该线程池中排队执行:

可以看到 SerialExecutor 线程池仅用于任务的排队, THREAD_POOL_EXECUTOR 线程池才是用于执行真正的任务,就是我们线程池部分讲到的 ThreadPoolExecutor :

再回到 executeOnExecutor() 方法中,那么 exec.execute(mFuture) 就是触发线程池开始执行任务的操作了。

那 executeOnExecutor() 方法中的 mWorker 是什么? mFuture 是什么?答案在 AsyncTask 的构造函数中:

原来 mWorker 是一个 Callable 对象, mFuture 是一个 FutureTask 对象,继承了 Runnable 接口。所以 mWorker 的 call() 方法会在 mFuture 的 run() 方法中执行,所以 mWorker 的 call() 方法在线程池得到执行!

同时 doInBackground() 方法就在 call() 中方法,所以我们自定义的耗时任务逻辑得到执行,不就是我们第二部分讲的那一套吗!

doInBackground() 的返回值会传递给 postResult() 方法:

就是通过 Handler 将最终的耗时任务结果从子线程发送到主线程,具体的过程是这样的, getHandler() 得到的就是 AsyncTask 构造函数中初始化的 mHandler , mHander 又是通过 getMainHandler() 赋值的:

可以在看到 sHandler 是一个 InternalHandler 类对象:

所以 getHandler() 就是在得到在主线程创建的 InternalHandler 对象,所以
就可以完成耗时任务结果从子线程到主线程的切换,进而可以进行相关UI操作了。
当消息是 MESSAGE_POST_RESULT 时,代表任务执行完成, finish() 方法被调用:

如果任务没有被取消的话执行 onPostExecute() ,否则执行 onCancelled() 。

如果消息是 MESSAGE_POST_PROGRESS , onProgressUpdate() 方法被执行,根据之前的用法可以 onProgressUpdate() 的执行需要我们手动调用 publishProgress() 方法,就是通过 Handler 来发送进度数据:

进行中的任务如何取消呢?AsyncTask 提供了一个 cancel(boolean mayInterruptIfRunning) ,参数代表是否中断正在执行的线程任务,但是呢并不靠谱, cancel() 的方法注释中有这么一段:

大致意思就是调用 cancel() 方法后, onCancelled(Object) 回调方法会在 doInBackground() 之后被执行而 onPostExecute() 将不会被执行,同时你应该 doInBackground() 回调方法中通过 isCancelled() 来检查任务是否已取消,进而去终止任务的执行!

所以只能自己动手了:

AsyncTask 整体的实现流程就这些了,源码是最好的老师,自己跟着源码走一遍有些问题可能就豁然开朗了!

4. Android创建子线程和回调主线程的几种方式

在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的两种,除此之外还有Content Provider和Broadcast Receiver)都会跑在这个Process。
一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。在这么多Thread当中,有一个Thread,我们称之为UI Thread。UI Thread在Android程序运行的时候就被创建,是一个Process当中的主线程Main Thread,主要是负责控制UI界面的显示、更新和控件交互。在Android程序创建之初,一个Process呈现的是单线程模型,所有的任务都在一个线程中运行。因此,我们认为,UI Thread所执行的每一个函数,所花费的时间都应该是越短越好。而其他比较费时的工作(访问网络,下载数据,查询数据库等),都应该交由子线程去执行,以免阻塞主线程。
那么,UI Thread如何和其他Thread一起工作呢?常用方法是:
诞生一个主线程的Handler物件,当做Listener去让子线程能将讯息Push到主线程的Message Quene里,以便触发主线程的handlerMessage()函数,让主线程知道子线程的状态,并在主线程更新UI。

5. 如何实现Android 中断线程的处理

可以用interrupt()方法中断线程,而线程是否已经中断则用Thread.currentThread().isInterrupted()方法返回true/false判断:
1、线程start()后马上调用interrupt(),在进入run()时中断标志已经被set on;
2、在处理sleep()可能抛出的InterruptedException时,再次中断线程即可成功中断;
3、注意interrupt()方法是set on 中断标志的,interrupted()方法是判断后并清除中断标志的。

public class ThreadDemo extends Thread{
public static void main(String[] args){
try{
ThreadDemo thread = new ThreadDemo();
thread.start();
thread.sleep(100);

thread.interrupt(); //中断线程
thread.sleep(100);

thread.printStr();
thread.interrupt(); //第三次中断线程

thread.printStr();
thread.join();
}catch(InterruptedException e){
e.printStackTrace();
}
}

private void printStr(){
System.out.println("thread obj");
}

@Override
public void run() {
while(!Thread.currentThread().isInterrupted()) {
System.out.println("thread running");

try {
Thread.sleep(100);
}catch(InterruptedException e)
{
System.out.println("InterruptedException");
Thread.currentThread().interrupt(); //再次中断线程
}
}

System.out.println("thread interrupted");
}
}
运行结果:
thread running
InterruptedException
thread interrupted
thread obj
thread obj

6. android studio 怎样使用多线程

有两种方式:

  1. new Thred类

  2. 用异步任务的方法

7. Android线程池ThreadPoolExecutor详解

       传统的多线程是通过继承Thread类及实现Runnable接口来实现的,每次创建及销毁线程都会消耗资源、响应速度慢,且线程缺乏统一管理,容易出现阻塞的情况,针对以上缺点,线程池就出现了。

       线程池是一个创建使用线程并能保存使用过的线程以达到复用的对象,简单的说就是一块缓存了一定数量线程的区域。

       1.复用线程:线程执行完不会立刻退出,继续执行其他线程;
       2.管理线程:统一分配、管理、控制最大并发数;

       1.降低因频繁创建&销毁线程带来的性能开销,复用缓存在线程池中的线程;
       2.提高线程执行效率&响应速度,复用线程:响应速度;管理线程:优化线程执行顺序,避免大量线程抢占资源导致阻塞现象;
       3.提高对线程的管理度;

       线程池的使用也比较简单,流程如下:

       接下来通过源码来介绍一下ThreadPoolExecutor内部实现及工作原理。

       线程池的最终实现类是ThreadPoolExecutor,通过实现可以一步一步的看到,父接口为Executor:

       其他的继承及实现关系就不一一列举了,直接通过以下图来看一下:

       从构造方法开始看:

       通过以上可以看到,在创建ThreadPoolExecutor时,对传入的参数是有要求的:corePoolSize不能小于0;maximumPoolSize需要大于0,且需要大于等于corePoolSize;keepAliveTime大于0;workQueue、threadFactory都不能为null。
       在创建完后就需要执行Runnable了,看以下execute()方法:

       在execute()内部主要执行的逻辑如下:
       分析点1:如果当前线程数未超过核心线程数,则将runnable作为参数执行addWorker(),true表示核心线程,false表示非核心线程;
       分析点2:核心线程满了,如果线程池处于运行状态则往workQueue队列中添加任务,接下来判断是否需要拒绝或者执行addWorker();
       分析点3:以上都不满足时 [corePoolSize=0且没有运行的线程,或workQueue已经满了] ,执行addWorker()添加runnable,失败则执行拒绝策略;
        总结一下:线程池对线程创建的管理,流程图如下:

       在执行addWorker时,主要做了以下两件事:
       分析点1:将runnable作为参数创建Worker对象w,然后获取w内部的变量thread;
       分析点2:调用start()来启动thread;
       在addWorker()内部会将runnable作为参数传给Worker,然后从Worker内部读取变量thread,看一下Worker类的实现:

       Worker实现了Runnable接口,在Worker内部,进行了赋值及创建操作,先将execute()时传入的runnable赋值给内部变量firstTask,然后通过ThreadFactory.newThread(this)创建Thread,上面讲到在addWorker内部执行t.start()后,会执行到Worker内部的run()方法,接着会执行runWorker(this),一起看一下:

       前面可以看到,runWorker是执行在子线程内部,主要执行了三件事:
       分析1:获取当前线程,当执行shutdown()时需要将线程interrupt(),接下来从Worker内部取到firstTask,即execute传入的runnable,接下来会执行;
       分析2:while循环,task不空直接执行;否则执行getTask()去获取,不为空直接执行;
       分析3:对有效的task执行run(),由于是在子线程中执行,因此直接run()即可,不需要start();
       前面看到,在while内部有执行getTask(),一起看一下:

       getTask()是从workQueue内部获取接下来需要执行的runnable,内部主要做了两件事:
       分析1:先获取到当前正在执行工作的线程数量wc,通过判断allowCoreThreadTimeOut[在创建ThreadPoolExecutor时可以进行设置]及wc > corePoolSize来确定timed值;
       分析2:通过timed值来决定执行poll()或者take(),如果WorkQueue中有未执行的线程时,两者作用是相同的,立刻返回线程;如果WorkQueue中没有线程时,poll()有超时返回,take()会一直阻塞;如果allowCoreThreadTimeOut为true,则核心线程在超时时间没有使用的话,是需要退出的;wc > corePoolSize时,非核心线程在超时时间没有使用的话,是需要退出的;
       allowCoreThreadTimeOut是可以通过以下方式进行设置的:

       如果没有进行设置,那么corePoolSize数量的核心线程会一直存在。
        总结一下:ThreadPoolExecutor内部的核心线程如何确保一直存在,不退出?
       上面分析已经回答了这个问题,每个线程在执行时会执行runWorker(),而在runWorker()内部有while()循环会判断getTask(),在getTask()内部会对当前执行的线程数量及allowCoreThreadTimeOut进行实时判断,如果工作数量大于corePoolSize且workQueue中没有未执行的线程时,会执行poll()超时退出;如果工作数量不大于corePoolSize且workQueue中没有未执行的线程时,会执行take()进行阻塞,确保有corePoolSize数量的线程阻塞在runWorker()内部的while()循环不退出。
       如果需要关闭线程池,需要如何操作呢,看一下shutdown()方法:

       以上可以看到,关闭线程池的原理:a. 遍历线程池中的所有工作线程;b. 逐个调用线程的interrupt()中断线程(注:无法响应中断的任务可能永远无法终止)
       也可调用shutdownNow()来关闭线程池,二者区别:
       shutdown():设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程;
       shutdownNow():设置线程池的状态为STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表;
       使用建议:一般调用shutdown()关闭线程池;若任务不一定要执行完,则调用shutdownNow();
        总结一下:ThreadPoolExecutor在执行execute()及shutdown()时的调用关系,流程图如下:

       线程池可以通过Executors来进行不同类型的创建,具体分为四种不同的类型,如下:

       可缓存线程池:不固定线程数量,且支持最大为Integer.MAX_VALUE的线程数量:

       1、线程数无限制
       2、有空闲线程则复用空闲线程,若无空闲线程则新建线程
       3、一定程度上减少频繁创建/销毁线程,减少系统开销

       固定线程数量的线程池:定长线程池

       1、可控制线程最大并发数(同时执行的线程数)
       2、超出的线程会在队列中等待。

       单线程化的线程池:可以理解为线程数量为1的FixedThreadPool

       1、有且仅有一个工作线程执行任务
       2、所有任务按照指定顺序执行,即遵循队列的入队出队规则

       定时以指定周期循环执行任务

       一般来说,等待队列 BlockingQueue 有: ArrayBlockingQueue 、 LinkedBlockingQueue 与 SynchronousQueue 。
       假设向线程池提交任务时,核心线程都被占用的情况下:
        ArrayBlockingQueue :基于数组的阻塞队列,初始化需要指定固定大小。
       当使用此队列时,向线程池提交任务,会首先加入到等待队列中,当等待队列满了之后,再次提交任务,尝试加入队列就会失败,这时就会检查如果当前线程池中的线程数未达到最大线程,则会新建线程执行新提交的任务。所以最终可能出现后提交的任务先执行,而先提交的任务一直在等待。
        LinkedBlockingQueue :基于链表实现的阻塞队列,初始化可以指定大小,也可以不指定。
       当指定大小后,行为就和 ArrayBlockingQueue一致。而如果未指定大小,则会使用默认的 Integer.MAX_VALUE 作为队列大小。这时候就会出现线程池的最大线程数参数无用,因为无论如何,向线程池提交任务加入等待队列都会成功。最终意味着所有任务都是在核心线程执行。如果核心线程一直被占,那就一直等待。
        SynchronousQueue :无容量的队列。
       使用此队列意味着希望获得最大并发量。因为无论如何,向线程池提交任务,往队列提交任务都会失败。而失败后如果没有空闲的非核心线程,就会检查如果当前线程池中的线程数未达到最大线程,则会新建线程执行新提交的任务。完全没有任何等待,唯一制约它的就是最大线程数的个数。因此一般配合Integer.MAX_VALUE就实现了真正的无等待。
       但是需要注意的是, 进程的内存是存在限制的,而每一个线程都需要分配一定的内存。所以线程并不能无限个。

阅读全文

与android线程实现方式相关的资料

热点内容
java程序员一天多少行代码 浏览:944
丧尸危机java 浏览:123
华为手机怎么去除app标记未读信息 浏览:852
java监控文件夹 浏览:803
群控服务器主机怎么转变普通电脑 浏览:705
手机怎么调整app大小 浏览:453
加密门禁卡揭秘 浏览:137
词释pdf 浏览:991
安卓手机上如何停止自动续费 浏览:880
加密编码摘要 浏览:785
疫情命令党 浏览:496
java转sql 浏览:705
android获取apn 浏览:74
phpfpm进程池 浏览:795
解压掏耳朵音频 浏览:676
爬香山解压 浏览:953
算法导论回溯 浏览:345
开盘指标源码查询 浏览:528
有道云保存服务器出错 浏览:641
生成360文件夹 浏览:1006