上一篇
上一篇博客写到了多进程的一些东西,实际上都是我把我手写的内容一篇一篇写到博客上,为什么这么傻要做这种事情呢?因为问我希望我能在手机上方便地去阅读,每一篇博客的量都是一张 A4 纸的正反面,我觉得内容还是适中吧,不要说读者会看我的博文,更希望的是自己有机会可以去回顾。上次面试的时候光顾着看什么 IPC、消息机制、view 事件,被问到基础内容心里都蒙了一下,千里之行还是始于足下啊!
七、中断
中断:一个线程执行完毕后会自动结束,运行时发生异常也会提前结束
-
InterruptedException
- 通过调用一个线程的 interrupt() 来中断该线程,如果该线程处于阻塞、限期等待或者无限等待状态,那么就会抛出 InterruptedExceptedException,结束线程。
- 注意不能中断 I/O 阻塞和 synchronized 锁阻塞(不然就异常了,冲入锁呢?JDK实现)
-
interrupt():线程方法,标志中断位(仅此而已,阻塞等待抛异常)
-
interrupted():判断线程是否处于中断状态
如果线程的 run 方法执行一个无限循环,应该检查标志位来跳出循环
-
和 Executor 关系:
- Executor 的 shutdown 会等待线程全执行完再关闭,而 shutdownNow() 会调用所有线程的 interrupt() 方法
- 如果只想中断一个线程,可以用 submit 方法提交线程,使用返回的 Future<?> 来关闭,使用其 cancel 方法来中断线程。
八、互斥同步
-
synchronized 由 JVM 实现(内置锁)
- 同步一个代码块:只作用于同一个对象
- 同步一个方法:同上
- 同步一个类:作用于整个类,不同对象只需要同类,则同步代码生效
- 同步一个静态方法:同上
-
ReentrantLock(显示锁)由 JDK 实现
优点:
- 获取和释放的灵活性
- 轮训锁和定时锁
- 公平性
ReentrantLock 方法:
- lock()
- tryLock()
- tryLock(long timeout, TimeUnit)
- unlock():注意在 finally 内释放锁
- newCondition()
比较
- 实现方式不同
- 性能,新 Java 对 synchronized 优化(如自选锁,性能大致相同)
- ReentrantLock 可中断锁,synchronized 不行
- ReentrantLock 可以是公平锁
- ReentrantLock 可以绑定多个条件
Condition 方法:
- await()
- await(long timeout, TimeUnit)
- singnal()
- singnalAll()
-
使用选择:优先使用 synchronized,JVM 支持不依赖 JDK,且不能被中断
-
volatile
- 保证此变量对所有线程可见,不能保证具有原子性(保存在内存)
- 禁止指令重排(详细内容请看 JVM)
- 读写性能几乎相同,写操作慢,需要加内存屏障
九、J.U.C-AQS 同步器
- CountDownLatch
- 倒计时门栓:用来控制一个线程等待多个线程
- 维护了一个计数器 cnt,每次调用 countDown() 会让计算器减1,减到 0 的时候,那些因为调用 CountDownLatch 的 await() 方法而在等待的线程就会被唤醒。
- CyclicBarrier
- 障栅:用来控制多个线程互相等待,只有但多个线程都到达时,这些线程才会继续执行,
- 可以 reset 循环使用
- 维护计数器,执行 await 会减 1,并进行等待,知道计数器为 0,所有等待线程执行
- Semaphore
- 信号量:可以控制对互斥资源的访问线程数
- 创建时指定信号量数量,通过 acquire() 获取,获取数量有限,为得到要等待
- 需要在 finally 里面 release
- 其他
- Phaser:可变计数障栅
- Exchanger:交换器(互相交换)
- SynchronousQueue:同步队列,固定方向,size = 0
十、AysncTaask
- 创建异步线程更简单(必须在 UI 线程创建)
- 三个泛型(传入、传递、传出)
- 方法:
- execute(…变长):执行
- onPreExecute()、onProgressUpdate(UI线程)、onPostExecute
- doInBackground(…):后台执行
- 原理分析(源码很复杂):
- 有状态控制
- 通过 Callable(mWorker)、Future、FutureTask(mFuture),交给异步线程执行
- 通过 Handler 将数据传出
十一、内存模型三大特性
- 原子性
- 可见性
- 有序性
十二、线程安全
-
不可变(final)
-
互斥同步(锁)
-
非阻塞式同步
-
解决现存阻塞和唤醒带来的性能问题
-
锁的分类
- 悲观锁:总是认为只要不去做正确的同步,就会出现问题
- 乐观锁:先进行操作,如果没有其他线程争用共享数据,那就成功了,否则采取补偿措施(不断地重试,直到成功为止)
-
CAS:比较并交换(compare-and-swap)
- 硬件支持两个步骤具有原子性
- CAS 指令有三个操作数:内存地址 V、就旧预期值 A 和新值 B,只有到 V 的值等于 A,才将 V 的值更新为 B
-
原子类操作
-
AtomocInteger 的方法调用了 Unsafe 类的 CAS 操作:xx.incrementAndGet()
-
LongAdder、LongAccumulator 只有当所有工作完成后再累加操作,减少重试次数
-
原子类更新
do { oldValue = largest.get(); newValue = Math(oldValue, observed); }while(!largest.compareAndGet(oldVaule, newValue));
-
-