android线程学习笔记

1.线程,进程

进程是程序在计算机的一次执行活动,进程是由进程控制块,程序,数据三部分组成的。进程是操作系统资源分配的的基本单位。
线程是cpu调度的基本单位,线程包含在进程中,共享进程的所有资源。一个进程中可以并发多个线程,也可以并行多个线程,取决于CPU是几核,是否使用了超线程技术

2.Android中线程的创建

一般来说在Android中有3中创建线程的的方式
方法一:自定义一个类继承Thread,重写run方法,然后在其他地方使用该对象的实例执行start方法就创建并启动了一个线程

static class Methon1 extends Thread{
        @Override
        public void run() {
            super.run();
            //do you work
        }
    }
 public static void main(String[] args) {
        new Methon1().start();
    }

方法二:使用接口Runnable。注意Runnable,只是一个回调接口,thread对象实现了Runnable,并且在run方法中做逻辑处理的,如下图所示:
在这里插入图片描述

new Thread(new Runnable() {
            @Override
            public void run() {
                //do you work
            }
        }).start();

Runnable只是一个任务对象,在调用了start后才创建了一个线程
方法三:自定义一个对象实现Callable,这里不能直接把这个对象作为参数传入到Thread中,需要借助FutureTask对象,取到返回值也需要使用futureTask的实例的get方法

 static class Methon3 implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            return 1;
        }
    }
public static void main(String[] args) {
        Methon3 methon3 = new Methon3();
        FutureTask<Integer> futureTask = new FutureTask(methon3);
        new Thread(futureTask).start();
        try {
            futureTask.get();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

3.synchronized,内置锁

synchronized关键字就我理解来说就是对标注的对象加了一把锁,加了synchronized关键字的地方如成员方法或属性,静态方法或属性这些资源就被标注了。上面说过同一进程下的线程共享进程的资源,如果不加synchronized关键字,进程的资源是允许被多个线程同时访问或读取或修改的,但有些资源是不允许被同时访问的,这就有了synchronized。被synchronized标注资源在线程访问时同时只允许同一个线程来访问,后来的线程不允许访问,必须等前一个线程访问完,释放了synchronized,才允许后来的线程进行访问
synchronized具有不公平性,可重入性的属性
不公平就是指,被synchronized标注的资源在释放后,由访问的线程自由竞争,不遵从顺序,先来先访问的规则。原因是因为线程在访问有线程正在使用被synchronized标注的资源时会被挂起,在重新被唤起时要经历一段CPU的时钟周期,如果按顺序来的话,每个线程都要经历这一段周期,无形之间会消耗大量的时间,性能降低。
可重入性
避免了下面这种情况的出现死锁

 public synchronized void doSomething() {
        doSomething(); // 调用自己类中其他的synchronized方法
}

4.Lock显示锁

Lock与synchronize相比,有一个trylock的方法,在线程阻塞是使用trylock,线程会在一定时间内阻塞尝试获取锁,根据是否获取返回false或true,在规定时间内没有获取锁,该线程不会一直阻塞,可以去做别的事

    public void ChangeKm(int km) {
        if (lock.tryLock()) {
            try {
                this.km = km;
                km_condition.signal();
            } finally {
                lock.unlock();
            }
        }
    }

等价于

public synchronized void ChangeKm(int km) {
                this.km = km;
                km_condition.signal();
}
  • lock()/unlock()方法使用比较多一个方法,就是用来获取锁和释放锁。获取锁时如果锁已被其他线程获取,则本线程阻塞等待。
  • lock()方法类似进入synchronized关系字修饰的方法或代码块。
  • unlock()方法类似执行完synchronized关系字修饰的方法或代码块。
  • lock()方法锁定后必须主动去释放锁,且在发生异常时,也不会自动释放锁。因此一般来说,
  • lock方法在try-catch块中使用,并且unlock()方法必须在finally块中,保证锁一定会被释放,防止死锁。
    lock使用了condition中定义的类似与synchronized的方法
private Lock lock = new ReentrantLock(true);
private Condition km_condition = lock.newCondition();
private Condition site_condition = lock.newCondition();

通过Lock的实例的newCondition可以得到condition的实例
在这里插入图片描述
await等价于wait,signal等价与notify

5.线程池的创建

为了提高资源利用率,一般都是使用线程池来维护线程的使用
jdk为我们提供了创建线程池的对象ThreadPoolExecutor
这里参考:https://www.cnblogs.com/zzuli/p/9386463.html

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

参数解释:

  • corePoolSize:核心线程池的大小,一般来说就是线程池的线程数。在工作中根据业务场景来设置值,是cpu密集型(计算多),还是I/O密集型(数据读取或者网络请求多),前一种就使用机器的核心数+1,使用Runtime.getRuntime().availableProcessors();来取得。后一种一般使用CPU核心数*2+1
  • maximumPoolSize:线程池能创建的最大线程数。如果线程池和任务缓存队列都满的情况下,新的任务进来就会创建新的线程来执行,会执行一些策略来执行
  • keepAlive :非核心线程能够空闲的最长时间,超过时间,线程终止。这个参数默认只有在线程数量超过核心线程池大小时才会起作用。只要线程数量不超过核心线程大小,就不会起作用。
  • TimeUnit:配合keepAlive,设置最长时间。如一秒:TimeUnit.SECONDS
  • BlockingQueue:任务队列。存放需要被执行的任务
  • ThreadFactory :threadFactory:线程工厂,用来创建线程,一般有三种选择策略。
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

RejectedExecutionHandler :拒绝处理策略,线程数量大于最大线程数就会采用拒绝处理策略,四种策略为

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

不过并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池
Excutors.newFixedThreadPool(int); //创建固定容量大小的缓冲池

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;

newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;

newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。

另外,如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写。

自用笔记,如有错误,欢迎提出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值