线程和线程池的用法

简介

线程是执行并发的单元,有自己的本地存储栈(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,FutureTaskRunnableFuture的实现类,RunnableFuture接口继承Runnable, Future接口,用来和Callable配合使用;

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);

以上是所有关于线程和线程池的基本用法;如有问题,请多指教!

源码

 

你可能感兴趣的:(Framework)