22. 线程同步的方法:sychronized、lock、reentrantLock等
在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时ReentrantLock是个不错的方案。
1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c) tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;
23. 锁的等级:对象锁、类锁
24. 写出生产者消费者模式
25. ThreadLocal的设计理念与作用(还不太懂)
ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
该类有1个默认构造函数,4个普通函数
26. ThreadPool用法与优势
合理利用线程池能够带来三个好处:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
使用:(以下为个人理解)
1.首先创建一个线程池,创建线程池有4种方法 ExecutorService mThreadPool
Excutors.newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
2.创建任务队列,将要执行的任务加入其中,通过轮询线程去除,让线程池去执行。LinkedList<Runnable> mTaskQueue;
3.创建轮询线程PoolThread,对线程池中的任务进行轮询,通过其Handler发消息给线程池去执行任务
4.创建轮询线程用的Handler
Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;
然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
然后ThreadPoolExecutor继承了类AbstractExecutorService。
在ThreadPoolExecutor类中有几个非常重要的方法:
execute():ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行
submit():在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。
shutdown();shutdownNow():用来关闭线程池的
27. Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等
障栅:CyclicBarrier.当所有等待的线程到达障栅后,才能继续向下运行。如一组数拆分n组排序,则要等这n组拍完后再合并,则每部分都排好了,可并行执行。
适用于线程数固定的情况。CyclicBarrier(int parties)其中parties为需要等待的线程个数。可循环使用。
倒计时门阀:CountDownLatch(int count),带计数开关的门,只有在门前等待的线程达到一定数量,门才会打开,线程才会执行。
count为初始值,线程调用await方法等待,方法countDown会导致计数值递减,计数值为0,所有在倒计时门阀范围内等待的线程的阻塞状态将解除。
与障栅不同点:1.不是所有线程都需要等待门阀打开;
2.门阀可由外部事件打开;
3.倒计时门阀是一次性的,一旦计数器为0,就不能再重复用它了。
信号量:一个信号量管理了一个许可集合。Semaphore(int permits),Semaphore(int permits,boolean fair),通过acquire获取一个许可,release释放一个许可。ImageLoader中就用到了信号量控制。
同步队列:SynchronousQueue.是一个没有数据缓冲的阻塞队列,在同步队列上的插入操作必须等待相应的删除执行完成后才能执行。不能调用peek方法来查看队列中是否有数据元素,也不允许对整个队列进行迭代遍历。
交换器:
阶段化处理: