简介
线程是执行并发的单元,有自己的本地存储栈(ThreadLocal),存储参数和局部变量,所有的APP启动的时候,都至少有一个线程(主线程)在运行。系统提供了两种开启线程的方式:1)继承Thread,重写run()方法;2)在Thread的构造函数传入Runnable;然后调用start()方法开启线程。每一个线程都有一个优先级影响系统调用线程的先后顺序;可以通过setPriority(int )方法设置线程的优先级,默认提供三种级别MAx_PRIORITY=10(最高级别),MIN_PRIORITY=1(最低级别),NORM_PRIORITY=5(正常级别)三种级别。
用法
开启子线程一共有三种方式,分别如下:
1)继承Thread;
public void startThread() {
MyThread myThread = new MyThread("线程1");
myThread.start();
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
try {
Log.e(TAG, Thread.currentThread().getName() + "开始执行");
sleep(3000);
} catch (InterruptedException e) {
e.getStackTrace();
}
}
}
2)在Thread的构造函数传入Runnable;
public void start() {
MyRunnable myRunnable = new MyRunnable();
t1 = new Thread(myRunnable);
t1.start();
}
class MyRunnable implements Runnable {
@Override
public void run() {
Log.e(TAG, Thread.currentThread().getName() + "开始执行");
Log.e(TAG, "State::" + t1.getState());
try {
Thread.sleep(3000);
} catch (Exception e) {
e.getStackTrace();
}
}
}
两种方式的区别:在于在多线程访问同一资源的情况下,用Runnable接口创建的线程可以处理同一资源,而用Thread类创建的线程则各自独立处理,各自拥有自己的资源;所以大多数的情况下,用方式2)开启子线程;
Thread也是Runnable接口的实现类;
3)--Callable,Future和FutureTask
public interface Callable {
V call() throws Exception;
}
Callable接口和Runnable接口的功能相似,都是用来开启开启子线程,Callable是一个参数化的类型接口,里面只有一个call方法,该方法可以有返回值V,也可以抛出异常。参数的类型和返回值的类型一致;
public interface Future {
//是否可以中断,
boolean cancel(boolean mayInterruptIfRunning);
//是否已经被取消
boolean isCancelled();
//是否执行完成
boolean isDone();
//用来返回运算的最后结果,MyCallable的call()方法返回的结果,如果计算还未完成,则会被阻塞,后面的方法也不会执行
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
Future接口,其作用就是用来保存异步运算的结果和启动一个异步运算;与Callable接口相配套使用;同时系统提供FutureTask,FutureTask是RunnableFuture的实现类,RunnableFuture接口继承Runnable, Future
final MyCallable myCallable = new MyCallable("MyCallable调用");
final FutureTask futureTask = new FutureTask(myCallable) {
@Override
protected void done() {
Log.e(tag, "FutureTask done。。.");
}
};
Log.e(tag, "Thread..........");
Thread thread = new Thread(futureTask);
thread.start();
try {
//用来返回运算的最后结果,MyCallable的call()方法返回的结果,如果计算还未完成,则会被阻塞,后面的方法也不会执行
String s1 = futureTask.get();
Log.e(tag, "futureTask.get()" + s1);
} catch (Exception e) {
e.getStackTrace();
}
Log.e(tag, "Thread.........start.");
class MyCallable implements Callable {
private String str;
public MyCallable(String str) {
this.str = str;
}
@Override
public String call() throws Exception {
Log.e(tag, str);
Thread.sleep(3000);
return "MyCallable call执行完成";
}
}
运行结果
根据运行结果分析:调用start()方法之后,调用MyCallable的call()方法,在call()方法执行结束后,return返回值,futureTask.get()得到call()方法return的返回值;FutureTask的done()执行,表示任务执行完成;在任务没有执行完成的情况下,get()方法处于阻塞状态;
子线程更新UI
总共有四种方式,都是通过Handler消息机制调用sendMessage系列或者post系列的方法发送消息到消息;
private void updateUI() {
1)
myHandler.sendEmptyMessage(1);
2)
myHandler.postDelayed(new Runnable() {
@Override
public void run() {
tvstart.setText("postDelayed");
}
}, 2000);
3)
//内部原理是handler发送延时消息,View中的handler发送Runnable到消息队列;
tvstart.postDelayed(new Runnable() {
@Override
public void run() {
tvstart.setText("通过View postDelayed");
}
}, 2000);
4)
//内部先判断是否为主线程,不是的话,内部的Handler post(Runnable action)
runOnUiThread(new Runnable() {
@Override
public void run() {
tvstart.setText("runOnUiThread");
}
});
}
方法3)的源码
public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
//用来处理没有Handler依附的情况
ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
return true;
}
方法4)的源码
public final void runOnUiThread(Runnable action) {
//当前线程不是主线程
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
线程创建并执行,在任务结束之后,GC回收线程,在并发不多的情况下,还很好;但是在需要大量开启线程的情况下,不断地创建新线程,会导致系统性能严重下降;解决办法:重用已有的线程,减少线程的创建,用线程池(Executor);
线程池
线程池的优点:
1:线程的创建和销毁由线程池维护,一个线程在完成任务后并不会立即销毁,而是由后续的任务复用这个线程,从而减少线程的创建和销毁,节约系统的开销
2:线程池旨在线程的复用,这就可以节约我们用以往的方式创建线程和销毁所消耗的时间,减少线程频繁调度的开销,从而节约系统资源,提高系统吞吐量
3:在执行大量异步任务时提高了性能
4:Java内置的一套ExecutorService线程池相关的api,可以更方便的控制线程的最大并发数、线程的定时任务、单线程的顺序执行等;
ThreadPoolExecutor的构造函数
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,
RejectedExecutionHandler handler){
//corePoolSize:核心线程数,能够同时执行的任务数,即是空闲也不会回收,除非设置allowsCoreThreadTimeOut为true,默认为false;
//maximumPoolSize:线程池允许的最大线程数
//keepAliveTime:保持活动时间,空闲的线程在超过keepAliveTime时间内没有任务就被销毁
// unit:时间单位
// workQueue:任务队列,用来存储已经被提交,没有执行的任务;
//threadFactory:线程工厂,用来创建线程池中的线程
//handler:拒绝策略,线程池关闭,或者最大线程数和队列已经饱和的情况下,抛出RejectedExecutionException异常;
}
线程池的种类
系统Executors类中提供了多种线程池
1)newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
newFixedThreadPool的核心线程数和最大线程数都是固定的,既不会新建也不会销毁线程,执行任务队列中的任务,任务队列的容量为Interger.MAX_VALUES;
2) newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));//无界的队列
}
newSingleThreadExecutor的单一数量线程池的核心线程数和最大线程数都是1,可以作为固定线程数量线程的一种特例;
3)newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());//直接提交队列
}
newCachedThreadPool没有核心线程,线程池允许最大的线程数为Interger.MAX_VALUES,在需要的时候,可以创建线程,在保持活动时间内有新任务执行会被不断的重复使用,在线程空闲60秒之后,没有新任务执行,线程会被销毁回收;该线程池适用于大量短暂的异步任务,显著提高程序性能;
4)newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS,MILLISECONDS,
new DelayedWorkQueue());//等待队列
}
newScheduledThreadPool创建指定固定数量核心线程,非核心线程的保持活动时间10毫秒,用于创建延时任务;
5)newSingleThreadScheduledExecutor
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
}
可以作为newScheduledThreadPool的一种特例;核心线程的数量为1;
线程池的用法
1)
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 50; i++) {
executorService.execute(myRunnable);
}
2)
MyThread myThread = new MyThread("线程1");
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 50; i++) {
executorService.execute(myThread);
}
线程池的优化
线程池很好地提高了系统的性能,但是创建线程池也需要资源,线程池内的线程数量的大小也会影响性能,线程数量多了会浪费资源,影响系统性能,少了会影响系统的吞吐量,通常来说我们要考虑的因素有CPU的数量、内存的大小、并发请求的数量等因素,按需调整;参考AsynTask的线程池参数设置;根据CPU的核数,确定核心线程数和最大线程数;
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU的核数
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());
}
};
private static final BlockingQueue sPoolWorkQueue =new LinkedBlockingQueue(128); //消息队列最大容量
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
以上是所有关于线程和线程池的基本用法;如有问题,请多指教!
源码