您的位置:首页 > 移动开发 > Android开发

Android 中多线程的简单使用

2016-05-10 13:38 686 查看
一、多线程的实现

1.最简单的启动一下新线程

private void startNewThread(){
new Thread(){
@Override
public void run() {
//耗时操作
}
}.start();
}


或者:

private void startNewThread(){
new Thread(new Runnable() {
@Override
public void run() {
//耗时操作
}
}){}.start();
}


实际上查看源码可以知道:Thread也是一个Runable,它实现Runable接口,在Thread类中有一个Runable类型的target字段,代表这个要执行在这个子线程中的任务。

Thread中默认的run方法源码为:

@Override
public void run() {
if (target != null) {
target.run();
}
}


2.线程中waite、sleep、join、yield方法

wait(): 当一个线程执行到wait() 方法时,它就进入到了一个和该对象相关的等待池中,同时失去(释放)了对象的机锁,使得其他线程可以访问。用户可以使用notify,notifyAll或者指定睡眠时间来唤醒当前等待池中的线程。

sleep(): 该方法时Thread的静态函数,作用是使调用线程进入睡眠状态。因为sleep()是Thread()类的static 方法。因此他不能改变对象的机锁。所以当一个synchronized快中调用sleep()方法时,线程虽然睡眠了,但是对象的机锁并没有被释放,其他线程无法访问到这个对象。(即使睡着也持有这对象锁)

join(): 等待目标线程执行完成之后,再继续执行。

yield(): 线程礼让,目标线程由运行状态转换为就绪状态,也就是让出执行权限,让其他线程得以优先执行,但其他线程是否优先执行时未知的。

简单示例:

wait()示例:

public class MyClass {
//    用于等待、唤醒的对象
private static Object sLockObject = new Object();

static void waitAndNotityAll() {
System.out.println("主线程中执行");
//        创建并启动子线程
Thread thread = new WaiteThread();
thread.start();
long startTime = System.currentTimeMillis();
try {
synchronized (sLockObject) {
System.out.println("主线程等待");
sLockObject.wait();
}
} catch (Exception e) {

}

long timeMs = System.currentTimeMillis() - startTime;
System.out.println("主线程继续--等待耗时: " + timeMs + " ms");
}

//    等待线程
static class WaiteThread extends Thread {
@Override
public void run() {
try {
synchronized (sLockObject) {
Thread.sleep(3000);
sLockObject.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args){
waitAndNotityAll();
}
}
执行结果:
主线程中执行
主线程等待
主线程继续--等待耗时: 3001 ms


join() 示例:

public class JoinTest {
static void joinDemo(){
Worker worker1 = new Worker("worker-1");
Worker worker2 = new Worker("worker-2");
worker1.start();
System.out.println("启动线程1");

try {
//            调用worker1的join函数,主线程会阻塞直到worker1执行完成
worker1.join();
System.out.println("启动线程2");
worker2.start();
worker2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程继续执行");
}

static class Worker extends Thread{

public Worker(String name){
super(name);
}

@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("work in " +getName());
}
}

public static void main(String[] args){
joinDemo();
}
}

执行结果:
启动线程1
work in worker-1
启动线程2
work in worker-2
主线程继续执行


yield()示例:

public class YieldTest {

public static final int MAX = 5;

static class YieldThread extends Thread {
public YieldThread(String name) {
super(name);
}

public synchronized void run() {
for (int i = 0; i < MAX; i++) {
System.out.printf("%s priority: [%d]------> %d\n", this.getName(), this.getPriority(), i);
//                当i==2 是,调用当前线程的yield函数。
if (i == 2) {
Thread.yield();
}
}
}
}

static void yieldDemo() {
YieldThread t1 = new YieldThread("thread-1");
YieldThread t2 = new YieldThread("thread-2");
t1.start();
t2.start();
}

public static void main(String[] args) {
yieldDemo();
}

}
可能没有效果


3、与多线程相关的方法—–Callable、Future、FutureTask

Callable 与Runable的功能大致相似,不同的是Callable是一个泛型接口,它有一个泛型的参数V ,该接口有一个返回值(类型为V)的call 函数而Runable 的run()函数不能将结果返回给客户程序。Callable的声明如下:

public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}


而 Future为线程池制定了一个可管理的任务标准,它提供了对Runable或者Callable任务的执行结果进行取消,查询是否完成、获取结果、设置结果操作、分别对应cancel、isDone、get、set函数。get方法会阻塞,直到任务返回结果。future声明如下:

public interface Future<V> {
//取消任务
boolean cancel(boolean mayInterruptIfRunning);

//任务是否已经取消
boolean isCancelled();

//任务是否已经完成
boolean isDone();

//获取结果,如果任务未完成,则等待,直到完成,因此该函数会阻塞
V get() throws InterruptedException, ExecutionException;

//获取结果,如果还未完成那么等待,直达timeout 或者返回结果,该函数会阻塞
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}


Future 只是定义了一些规范的接口,而FutureTask则是它的实现类。FutureTask实现了RunableFuture< V > 而RunableFuture又 继承自 Runable,和Ruture< V > 这两个接口,因此FutureTask具备了他们的能力。FutureTask代码如下:

public class FutureTask<V> implements RunnableFuture<V> {
.......
}


public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}


FutureTask会像Thread包装Runable 那样包装 Callable,或者Runable

参考FutureTask的构造方法:

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;       // ensure visibility of callable
}


public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;       // ensure visibility of callable
}


其中第二个构造方法,调用Executors.callable()函数将一个runnable转换成一个实现Callable接口的类。参考代码:

public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
/**
* A callable that runs given task and returns given result
*/
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}


由于FutureTask实现了Runable ,因此,它既可以通过Thread包装来直接执行,也可以提交给ExcuteService来执行。并且还可以直接通过get()函数获取执行结果,该函数会阻塞,知道结果返回。因此FutureTask既是Future,Runable,又包装了Callable,(如果Runnable最终也会被转换为Callable),他是这两者的合体。

代码示例:

public class FutureDemo {
//    线程池
static ExecutorService mExecutor  = Executors.newSingleThreadExecutor();

public static void main(String[] args){
try {
futureWithRunable();
futureWithCallable();
futureTask();
}catch (Exception e){

}
}

private static void futureTask() throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask  =new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return fibc(20);
}
}) ;
//提交futureTask
mExecutor.submit(futureTask);
System.out.println("future result from futureTask : "+futureTask.get());
}

private static void futureWithCallable() throws ExecutionException, InterruptedException {
final Future<Integer> result2 = mExecutor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return fibc(20);
}
}) ;
System.out.println("future reuslt from callable: "+result2.get()) ;
}

private static void futureWithRunable() throws ExecutionException, InterruptedException {
//        提交runable,没有返回值,future没有数据
Future<?> result = mExecutor.submit(new Runnable() {
@Override
public void run() {
fibc(20);
}
});

System.out.println("future result from runable : "+result.get());
}

private static int fibc(int num){
if(num==0){
return 0;
}
if(num==1){
return 1 ;
}
return fibc(num-1) +fibc(num-2);
}
}


二、线程池

线程池的优点:

(1) 重用存在的线程,减少对象的创建,销毁的开销

(2)可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多的资环竞争,避免堵塞;

(3) 提供定时执行,定期执行,单线程、并发数控制等功能。

线程池都实现了ExecuteService接口,该接口定义了线程池需要实现的接口。如submit、excute、shutdow、等方法

它的实现有 ThreadPoolExecutor和ScheduledThreadPoolExecutor。

1、启动指定数量的线程——ThreadPoolExecutor

ThreadPoolExecutor的构造方法如下:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}


corePoolSize: 线程池所保存的核心线程数,线程池启动后默认是空的,只有任务来临时才会创建线程以处理请求。prestartCoreThreads方法可以在线程池启动后即启动所有核心线程一等待任务。

maximumPoolSize: 线程池允许创建的最大线程数,当workQueue使用无界队列时(如LinkedBlockingQueue),则此参数无效。它与corePoolSize的作用是调整“线程池中实际运行的线程数量” 。例如,当新任务提交给线程池时,如果线程池中的运行线程数量小于corePoolSize,则创建新线程来处理请求,如果此时,线程池中的运行线程数量大于corePoolSize,但小于maximumPoolSize,则仅当阻塞队列满时才创建新线程,如果设置的corePoolSize 和maximumPoolSize 相同,则创建了固定大小的线程池,如果将maxiumPoolSize设置为基本的无界值(如: Integer.MAX_VALUE),则允许线程池适应任意数量的并发任务。

keepAliveTime: 当前线程池线程总数大于核心线程数时,终止多余的空闲线程的时间。

Unit: keepAliveTime参数的时间单位,可选值有毫秒,秒,分等。

workerQueue: 任务队列,如果当前线程池,达到核心线程数corePoolSize,且当前所有线程都处于活动状态时,则将新加入的任务放到此队列中。

threadFactory: 线程工厂,让用户可以定制线程的创建过程,通常不需要设置。

Handle : 决绝策略,当线程池与workQueue队列都满了的情况下,对新任务采取的处理策略。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息