美文网首页程序员
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