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类进行重写。
自用笔记,如有错误,欢迎提出