说完这个线程,java部分就要结束了,就开始真正的Android路程就开始了,下一篇文章是Android的四大组件讲解。
1. 在学习线程之前需要知道的几个概念
多线程:一个进程运行时产生了多个线程,目的是为了节约CPU资源。
并发:是指能处理多个同时性活动的能力,并发事件之间不一定要同一时刻发生。
并行:同时发生两个并发事件。
线程安全:在并发的情况下,该代码经过多线程使用,线程的调度顺序不影响任何结果。
同步:Java中同步是指人为控制、保证共享资源的多线程访问成为线程安全,来保证结果准确。
2. 线程的状态
当一个线程完成一轮时,它移到可运行队列的尾部等待,直到它最终排队到该队列的前端,才能被再次选中。
锁是用来保护资源在并发环境下的数据安全。
做两件事情 :维护线程状态 , 阻塞或者唤起线程。
需要考虑的点:
1.竞争策略 (公平和非公平)
2.等待策略 (自旋等待或者挂起)
3.是否可重入 (持有锁的线程是否可以多次申请该锁)
4.是否可以被多个对象持有(互斥还是共享)
5.竞争者 (线程)
6.被竞争的资源 (锁 & 锁保护的数据)
7.condition
3. 多线程机制
monitor:解决同步问题的调度工具,每个对象都会内置这样一个监听器,用这个监听器来判断该线程继续执行还是阻塞。这个监听器只会在synchronized范围内才发挥作用,而在非多线程的情况下失效。
示例:生产者消费者模式
//生产者
public synchronized void producer(){
if (product >= PRODUCT_MAX){
try {
wait();
//缓冲池已满,停止生产
} catch (InterruptedException e) {
e.printStackTrace();
}
return;
}
product++;
Log.i(TAG,"生产者生产第"+product+"个产品");
notifyAll();
}
//消费者
public synchronized void consumer(){
if (product <= PRODUCT_MIN){
try {
wait();
//缓冲池没有产品,停止消费
} catch (InterruptedException e) {
e.printStackTrace();
}
product--;
Log.i(TAG,"消费者消费第"+product+"个产品");
notifyAll();
}
}
多线程模型:为了同时支持用户线程和内核线程,实现用户级线程和内核级线程的连接。main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去。
多线程模型有多对一、一对一、多对多模型。
volatile:每次针对该变量的操作都激发一次load and save。
多线程使用的变量要使用volatile或者final修饰,否则,如果B线程修改了这个变量的值,A线程看到的还是B线程修改之前的值。但如果线程是安全的,加volatile会牺牲性能,因为volatile就是不去缓存。
4. 基本线程类
Thread、Runnable、Callable
常用的多线程设计模式有:Future、Master-Worker、Guard Susperionsion、不变、生产者-消费者 模式。
5. 多线程控制类
ThreadLocal类:
ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
每个Thread都持有一个TreadLocalMap类型的变量,以本身为key,以目标为value。
主要方法是get()和set()。
容器类: BlockingQueue 和 ConcurrentHashMap:
是线程安全的类。阻塞队列,一般是向队尾添加新的元素,从队头取元素。
put() : 带阻塞的放元素,当队列中对象的个数大于所设置的最大值的时候,会阻塞 。
take() : 带阻塞的取元素,当队列中的对象个数等于0的时候会等待。
线程池(使用ThreadPoolExecutors创建):
为了解决频繁的创建线程,销毁线程,提高系统的开销。
execute()方法: 可以向线程池提交一个任务,交由线程池去执行
submit()方法: 这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。
shutdown()和shutdownNow()是用来关闭线程池的。
创建线程池应该使用 ThreadPoolExecutors,而不允许使用Executors 来构建线程池,因为FixedThreadPool 和 SingleThreadExecutor 允许的请求队列的最大长度为 Integer.MAX_VALUE, 可能会堆积大量的请求,从而导致OOM。