程序员

android里的多线程

2018-09-05  本文已影响0人  waiwaaa

在Android中,我们通常采用如下两个方法启动新的线程

private void startThread(){
    //第一种
    new Thread(){
        @Override
        public void run() {
            //do something
        }
    }.start();
    //第二种
    new Thread(new Runnable() {
        @Override
        public void run() {
            //do something
        }
    }).start();
}

两者的差别不大,实际上Thread是实现了Runnable接口的一个实现类,实际上执行的仍然是一个Runnable。

线程的4个方法

函数名 作用
wait() 线程进入等待池中,同时失去对象的机锁,使其它线程可以访问。用户可以使用notify,notifyAll或者指定睡眠时间来唤醒当前线程 注意:wait,notify,notifyAll必须放在synchronized block中,否则会抛异常
sleep() Thread的静态函数,作用是进入睡眠状态。它不能改变对象的机锁,即使睡眠了也持有对象锁。
join() 等待目标线程执行完成之后再继续执行
yield() 线程礼让。目标线程由运行状态转换为就绪状态,即让出执行权限,让其他线程得以优先执行,但其它线程是否优先执行是未知的

wait和notifyAll的运用示例:

public class Test {
     private static Object sLockObject=new Object();

    static void waitAndNotifyAll(){
        System.out.println("主线程运行");
        Thread thread=new WaitThread();
        thread.start();

        long startTime=System.currentTimeMillis();
        try {
            synchronized (sLockObject){
                System.out.println("线程等待");
                sLockObject.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long time=System.currentTimeMillis()-startTime;
        System.out.println("耗时:"+time+" ms");
    }

    static class WaitThread extends  Thread{
        @Override
        public void run() {
            System.out.println("waitThread 开始");
            try {
                synchronized (sLockObject){
                    Thread.sleep(3000);
                    sLockObject.notifyAll();
                }
            }catch (Exception e){

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

}

输出结果:

主线程运行
线程等待
waitThread 开始

wait让主线程执行等待,释放机锁后WaitThread得以运行,WaitThread中调用notifyAll后,主线程继续执行。

join阻塞当前调用join函数的线程,直到执行完毕后继续。

示例:

public class Join {
    public static void main(String[] args) {
        Worker worker1=new Worker("worker1");
        worker1.start();
        System.out.println("work1 start");
        try {
            worker1.join();
            Worker worker2=new Worker("worker2");
            worker2.start();
            System.out.println("work2 start");
            worker2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    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());
        }
    }
}

输出信息

work1 start
work in worker1
work2 start
work in worker2

可以看出join会阻塞主线程。

yield会主动让出线程控制权

public class Yield {
    public static void main(String[] args) {
        YieldThread y1=new YieldThread("yield-1");
        YieldThread y2=new YieldThread("yield-2");
        
        y1.start();
        y2.start();
    }
    
    static class YieldThread extends Thread{
        public YieldThread(String name){
            super(name);
        }
        @Override
        public synchronized void run(){
            for(int i=0;i<5;i++){
                System.out.printf("%s, priority %d --->%d\n",this.getName(),this.getPriority(),i);
                
                if(i==2)Thread.yield();
            }
        }
    }
}

输出结果

yield-2, priority 5 --->0
yield-1, priority 5 --->0
yield-2, priority 5 --->1
yield-2, priority 5 --->2
yield-1, priority 5 --->1
yield-1, priority 5 --->2
yield-2, priority 5 --->3
yield-1, priority 5 --->3
yield-1, priority 5 --->4
yield-2, priority 5 --->4

与多线程相关方法

public interface Callable<V>{
  V call() throws Exception;
}

Runnable、Callable、FutureTask示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class FutureDemo {
    static ExecutorService mExecutor=Executors.newSingleThreadExecutor();
    
    public static void main(String[] args) {
        try {
            futureWithRunnable();
            futureWithCallable();
            futureTask();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    
    private static void futureWithRunnable() throws InterruptedException, ExecutionException{
        Future<?> result = mExecutor.submit(new Runnable(){
            @Override
            public void run() {
                fibc(20);
            }
        });
        System.out.println("future result from runnable:"+result.get());
    }
    
    private static void futureWithCallable() throws InterruptedException, ExecutionException{
        Future<Integer> result = mExecutor.submit(new Callable<Integer>(){
            @Override
            public Integer call() throws Exception {
                return fibc(20);
            }});
        System.out.println("future result from callable:"+result.get());
    }
    
    private static void futureTask() throws InterruptedException, ExecutionException{
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>(){
            @Override
            public Integer call() throws Exception {
                return fibc(20);
            }});
        mExecutor.submit(futureTask);
        
        System.out.println("future result from futureTask:"+futureTask.get());
    }
    
    
    private static int fibc(int num){
        if(num==0)return 0;
        if(num==1)return 1;
        return fibc(num-1)+fibc(num-2);
    }

}

执行结果

future result from runnable:null
future result from callable:6765
future result from futureTask:6765

线程池

通过线程池的统一调度、管理使得多线程的使用更简单、高效。
线程池都实现了ExecutorService接口,它的实现有ThreadPoolExecutor和ScheduledThreadPoolExecutor。
通常我们不会用new的形式创建线程池,而是用Executors工厂类来生成。
四大线程池:

同步集合

函数名 作用
add(e) 把元素加入到BlockingQueue,可以容纳则返回true,否则抛出异常
offer(e) 将元素加到BlockingQueue,可以容纳则返回true,否则返回false
offer(e,time,unit) 将元素加到BlockingQueue,可以容纳则返回true,否则再等待指定的时间后再尝试添加,失败则返回false
put(e) 将元素加到BlockingQueue,如果不能容纳则会阻塞线程直到里面可以继续添加
take() 取走排在队首的对象,若队列为空则进入等待状态直到有新对象加入为止
poll(time,uint) 取出并移除队首元素,设定的时间内没有获取到则返回null
element() 获取队首元素,如果队列为空则抛出NoSuchElementException异常
peek() 获取队首元素,队列为空则返回 null
remove() 获取并移除队首元素,队列为空则抛出NoSuchElementException异常

BlockingQueue在Android的实现有:

ArrayBlockingQueue数组实现的、纯种安全的、有界的阻塞队列。按先进先出原则进行排序,从尾部插入,从头部开始返回
LinkedBlockingQueueb 单向链表的阻塞队列。按先进先出原则进行排序,从尾部插入,从头部开始返回
LinkedBlockingDeque双向链表实现的双向并发阻塞队列。同时支持先进先出和先进后出两种操作,可以从队列的头和尾同进操作(插入/删除)。

同步锁

基本操作

函数 作用
lock() 获取锁
tryLock() 尝试获取锁
tryLock(longtimeout,timeUnit) 尝试获取锁,如果指定时间还获取不到那么超时
unlock() 释放锁
newCondition() 获取锁的Condition

使用ReentrantLock的一般Lock、tryLock与unlock成对出现。

Lock lock = new ReentrantLock();
public void dosomething(){
  lock.lock();
  try{
  } finally {
    // unlock必须在finally中调用,防止catch
    lock.unlock();
  }
}

Condition

函数 作用
await() 线程等待
await(int time,TimeUnit) 线程等待指定时间,超过时间则为超时
signal() 随机唤醒某个等待线程
signalAll() 唤醒所有等待中的线程
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    public static void main(String[] args) {
        final ExecutorService executorService=Executors.newFixedThreadPool(3);
        final Semaphore semaphore=new Semaphore(3);
        
        for(int i=0;i<5;i++){
            executorService.submit(new Runnable(){
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println("剩余许可:"+semaphore.availablePermits());
                        Thread.sleep(2000);
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }});
        }
    }
}

运行结果:

剩余许可:1
剩余许可:1
剩余许可:0
剩余许可:2
剩余许可:1
第一次输出前3条,2秒后输出后面两条。

循环栅栏CyclicBarrier

一个同步辅助类,允许一组线程互相等待,直到达到公共屏障点。通过示例理解:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {    
    private static final int SIZE = 5;
    private static CyclicBarrier mCyclicBarrier;

    public static void main(String[] args) {
        mCyclicBarrier = new CyclicBarrier(SIZE, new Runnable(){
            @Override
            public void run() {
                System.out.println("-->满足条件,执行操作。参与者:"+mCyclicBarrier.getParties());
            }});
        
        //
        for(int i=0;i<SIZE;i++){
            new WorkThread().start();
        }
    }
    
    static class WorkThread extends Thread{
        public void run(){
            try {           
                System.out.println(Thread.currentThread().getName()+" 等待CyclicBarrier.");
                mCyclicBarrier.await();
                System.out.println(Thread.currentThread().getName()+"继续");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

}

执行结果:

Thread-0 等待CyclicBarrier.
Thread-3 等待CyclicBarrier.
Thread-2 等待CyclicBarrier.
Thread-1 等待CyclicBarrier.
Thread-4 等待CyclicBarrier.
-->满足条件,执行操作。参与者:5
Thread-4继续
Thread-0继续
Thread-1继续
Thread-3继续
Thread-2继续

从示例可以看出,当await的个数达到5后,会先调用CyclicBarrier里的Runnable,然后5个线程会一起继续执行。相当于5个一组,5个一组的等待执行。

闭锁CountDownLatch

也是一个同步辅助类,在完成一组正在其它线程中执行的操作之前,它允许一个或多个线程一直等待,直到条件被满足。
代码示例:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    private static final int LATCH_SIZE = 5;
    
    public static void main(String[] args) {
        try {
            CountDownLatch latch=new CountDownLatch(LATCH_SIZE);
            for(int i=0;i<LATCH_SIZE;i++){
                new WorkerThread(latch).start();
            }
            
            System.out.println("主线程等待");
            latch.await();
            System.out.println("主线程继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class WorkerThread extends Thread{
        CountDownLatch mLatch;
        public WorkerThread(CountDownLatch latch){
            mLatch=latch;
        }
        public void run(){
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"执行操作");
                mLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

主线程等待
Thread-3执行操作
Thread-4执行操作
Thread-1执行操作
Thread-2执行操作
Thread-0执行操作
主线程继续执行

AsyncTask

它对Thread+Handler的良好封装,减少了开发者处理问题的复杂度,提高了开发效率

//调用task
for (int i =0;i<6;i++){
      new MyTask().execute("task"+i);
 }

class MyTask extends AsyncTask<String,Integer,String>{
    @Override
    protected String doInBackground(String... strings) {
        String name=strings[0];
        System.out.println(name+" start1");
        SystemClock.sleep(1000);
        System.out.println(name+" start2");
        SystemClock.sleep(1000);
        System.out.println(name+" end");
        return "s";
    }
}

09-05 10:53:48.014 25644-25974/com.yy.itemtouchhelper I/System.out: task0 start2
09-05 10:53:49.049 25644-25974/com.yy.itemtouchhelper I/System.out: task0 end
09-05 10:53:49.052 25644-26064/com.yy.itemtouchhelper I/System.out: task1 start1
09-05 10:53:50.086 25644-26064/com.yy.itemtouchhelper I/System.out: task1 start2
09-05 10:53:51.126 25644-26064/com.yy.itemtouchhelper I/System.out: task1 end
09-05 10:53:51.128 25644-26120/com.yy.itemtouchhelper I/System.out: task2 start1
09-05 10:53:52.148 25644-26120/com.yy.itemtouchhelper I/System.out: task2 start2
09-05 10:53:53.173 25644-26120/com.yy.itemtouchhelper I/System.out: task2 end
09-05 10:53:53.174 25644-26120/com.yy.itemtouchhelper I/System.out: task3 start1
09-05 10:53:54.210 25644-26120/com.yy.itemtouchhelper I/System.out: task3 start2
09-05 10:53:55.214 25644-26120/com.yy.itemtouchhelper I/System.out: task3 end

可以看出,AsyncTask是采用一个线程来串行执行任务的,如果要并行执行任务,可以通过调用AsyncTask的executeOnExecutor来实现。

上一篇 下一篇

猜你喜欢

热点阅读