扩展文章
非主线程中能不能直接new Handler()
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
JAVA中线程同步的方法
关于AsyncTask、HandlerThread的理解
关于Service,IntentService的理解
扩展知识
-
线程和进程有什么区别?
一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。 -
多线程编程的好处是什么?
多个线程被并发的执行以提高程序的效率,提高CPU和内存利用率
-
什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)?
线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。 -
什么是ThreadLocal?
ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。
ThreadLocal是数据的隔离,但是并非数据的复制,而是在每一个线程中创建一个新的数据对象,然后每一个线程使用的是不一样的。
每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。
- 什么是线程池
一个线程池管理了一组工作线程,同时它还包括了一个用于放置等待执行的任务的队列。
- 创建线程
JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。
1、继承Thread类实现多线程
继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
在合适的地方启动线程如下:
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
2、实现Runnable接口方式实现多线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口,如下:
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
public void run() {
if (target != null) {
target.run();
}
}
3、使用ExecutorService、Callable、Future实现有返回结果的多线程。
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。下面提供了一个完整的有返回结果的多线程测试例子
注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待
Log.w("TAG", "----程序开始运行----");
Date date1 = new Date();
int taskSize = 2;
// 创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i + " ");
// 执行任务并获取Future对象
Future f = pool.submit(c);
// System.out.println(">>>" + f.get().toString());
list.add(f);
}
// 关闭线程池
pool.shutdown();
// 获取所有并发任务的运行结果
for (Future f : list) {
// 从Future对象上获取任务的返回值,并输出到控制台
try {
Log.w("TAG", ">----123-->>" + f.get().toString());
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Date date2 = new Date();
Log.w("TAG", "----程序结束运行----,程序运行时间【" + (date2.getTime() - date1.getTime()) + "毫秒】");
}
public class MyCallable implements Callable<Object> {
private String taskNum;
public MyCallable(String taskNum) {
this.taskNum = taskNum;
}
public Object call() throws Exception {
Log.w("TAG", ">call---->>" + taskNum + "任务启动");
Date dateTmp1 = new Date();
Thread.sleep(3000);
Date dateTmp2 = new Date();
long time = dateTmp2.getTime() - dateTmp1.getTime();
Log.w("TAG", ">>>" + taskNum + "任务终止");
return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";
}
}
执行结果,两个任务中止后,将结果返回
W/TAG: ----程序开始运行----
W/TAG: >call---->>0 任务启动
W/TAG: >call---->>1 任务启动
W/TAG: >>>0 任务终止
W/TAG: >----123-->>0 任务返回运行结果,当前任务时间【3006毫秒】
W/TAG: >>>1 任务终止
W/TAG: >----123-->>1 任务返回运行结果,当前任务时间【3005毫秒】
W/TAG: ----程序结束运行----,程序运行时间【3029毫秒】
关于线程池
ExecutorService与生命周期
ExecutorService扩展了Executor并添加了一些生命周期管理的方法。一个Executor的生命周期有三种状态,运行 ,关闭 ,终止 。Executor创建时处于运行状态。当调用ExecutorService.shutdown()后,处于关闭状态,isShutdown()方法返回true。这时,不应该再想Executor中添加任务,所有已添加的任务执行完毕后,Executor处于终止状态,isTerminated()返回true。
如果Executor处于关闭状态,往Executor提交任务会抛出unchecked exception RejectedExecutionException。
Java通过Executors提供四种线程池,分别为:
- CachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。在线程空闲60秒后终止线程。
②最大线程数Integer.MAX_VALUE,最多65535个线程
③超时时间60s - ScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
- FixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- SingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
②最大线程数Integer.MAX_VALUE,最多65535个线程
他就是线程数量为1的FixedThreadPool,如果向SingleThreadExecutor提交多个任务,那么这些任务会排队,每个任务都会在下个任务开始之前就结束,所有任务都用一个线程,并且按照提交的顺序执行。
①一个核心线程
具体的注释可以点击进去参考源码,
ExecutorService pool1 = Executors.newCachedThreadPool();
ExecutorService pool2 = Executors.newFixedThreadPool(3);
ExecutorService pool3 = Executors.newScheduledThreadPool(3);
ExecutorService pool4 = Executors.newSingleThreadExecutor();
Executors类用于管理Thread对象,简化并发过程,我们可以看到FixedThreadPool的创建过程:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
显然这四种线程池方法都是返回实现了ExecutorService接口的ThreadPoolExecutor。
Executor是Java中的概念,是一个接口,真正的线程池实现是ThreadPoolExecutor。它提供了一系列的参数来配置不同的线程池。当然我们也可以直接用ThreadPoolExecutor来自定义更灵活的线程池。
public ThreadPoolExecutor(
int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime, //非核心线程的超时时间
TimeUnit unit,//单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory//线程工厂
RejectedExecutionHandler handler)
- corePoolSize
核心线程数,核心线程池默认会在线程池中一直存活,无论它是不是闲置。如果将ThreadPoolExecute的allowCoreThreadTimeOut设置为true,那么闲置的核心线程会执行超时策略,这个超时策略的时间间隔是由keepAliveTime所指定的。 - maximumPoolSize
线程池所能容纳的最大线程数,当活动的线程数达到这个数值后,后续的任务会被阻塞。 - keepAliveTime
非核心线程池的超时时长,非核心线程池闲置的时间超过这个时间就会被回收。当ThreadPoolExecute的allowCoreThreadTimeOut设置为true时,它同样也作用于核心线程。 - unit
用于指定keepAliveTime的单位。 - workQueue
线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会储存在这个参数中。 - threadFactory
线程工厂,为线程池提供创建新线程的功能,ThreadFactory是一个接口,他只有一个方法:
public interface ThreadFactory {
Thread newThread(Runnable var1);
}
除了这些参数外还有个很不常用的参数RejectedExecutionHandler handler。当线程池无法执行新任务时(任务队列满了或者无法成功执行)会调用handler的rejectExecutionException。
执行步骤Android多线程通信机制
- Android线程间通信方式之AsyncTask机制
- Handler机制;
- runOnUiThread方法,用Activity对象的runOnUiThread方法更新,在子线程中通过runOnUiThread()方法更新UI
- View.post(Runnable r) 、
网友评论