美文网首页程序员
android里的多线程

android里的多线程

作者: waiwaaa | 来源:发表于2018-09-05 12:19 被阅读0次

    在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

    与多线程相关方法

    • Runnable 既能运行在Thread中,也能运行在线程池中。其它几个只能运行在线程池中。
    • Callable 一个泛型接口,有一个返回值
    public interface Callable<V>{
      V call() throws Exception;
    }
    
    • Future 为线程池制定了一个可管理的任务标准。提供对Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作,分别对应cancel、isDonw、get、set函数。get方法会阻塞,直到任务返回结果。
    • FutureTask 对Future的实现类。FutureTask实现了RunnableFuture<V>,而RunnableFuture<V>实现了Runnable、Futrue<V>两个接口。

    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工厂类来生成。
    四大线程池:

    • fixThread 正规线程池,定长线程池,超出会等待,无超时,队列大小不限,关闭才会回收
    • CacheTheadPool 缓存线程池,线程数大,为每个任务任务添加新线程,60秒没用到就回收。
    • SingleThreadPool 单线程池,里面的线程要排队等待,不处理并发,不会被回收。
    • ScheduledThreadPool 唯一有延迟执行和周期执行的线程池。核心线程池固定,非核心线程没有限制,但闲时立即回收。

    同步集合

    • CopyOnWriteArrayList 多线程共享同一列表的并发容器,另外并发容器还有CopyOnWriteArraySet
    • ConcurrentHashMap HashTable是HashMap的线程安全实现,但它使用synchronized来保证线程安全,效率低。ConcurrentHashMap采用锁分段技术提升效率。
    • BlockingQueue
    函数名 作用
    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双向链表实现的双向并发阻塞队列。同时支持先进先出和先进后出两种操作,可以从队列的头和尾同进操作(插入/删除)。

    同步锁

    • synchronized 能作用于对象、函数、class。每个对象都只有一个锁,谁拿到谁有访问权,当synchronized作用于函数时,实际上也锁的也是对象,即函数据在的对象。
    • 显示锁--ReentrantLock与Condition
      ReentrantLock和内置锁synchronized实现了相同的语义,但具有更高的灵活性。显示锁将锁的获取和释放分开,同时可以提供轮训锁和定时锁,同时可以提供公平锁或非公开锁。

    基本操作

    函数 作用
    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() 唤醒所有等待中的线程
    • 信号量Semaphore
      Semaphore是一个计数信号量,它的本质是一个“共享锁”。它可以指定同时持有许可的个数,通过acquire获取许可,通过release释放许可,超过许可则等待。
    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的良好封装,减少了开发者处理问题的复杂度,提高了开发效率

    • AsyncTask对象必须在主线程中创建
    • AsyncTask对象的execute方法必须在主线程中调用
    • 不要在程序中直接调用onPreExeute()onPosExeutedoInBackgroundonPressessUpdate方法
    • 一个AsyncTask对象只能调用一次execute方法
    //调用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来实现。

    相关文章

      网友评论

        本文标题:android里的多线程

        本文链接:https://www.haomeiwen.com/subject/hvkbwftx.html