一,线程同步
1.1.1线程同步机制简介
线程同步机制是一套用于协调线程之间数据访问的机制,该机制可以保障线程安全。
java平台提供的线程同步机制包括:锁,volatile关键字,final关键字,static关键字,以及相关API,如Object.wait()/Object.notify()等
1.1.2锁概述
线程安全问题的产生前提是多个线程并发访问共享数据。
将多个线程对于共享数据的访问从并发转换为串行访问,即一个共享数据一次只能被一个线程访问,锁就是复用种思路来保障线程数据安全的
锁(Lock)可以理解为对共享数据保护的一个许可证,对于同一个许可证的共享数据来说,任何线程想要访问这些共享数据必须先持有该许可证,一个线程只有在持有许可证的情况下才能对这些共享数据进行访问,并且一个许可证只能被一个线线程持有,许可证持有的线程在结束对共享数据的访问后必须释放其持有的许可证
一个线程在访问共享数据之前必须先获得锁,获得锁的线程称为锁的持有线程,一个锁一次只能被一个线程持有,锁的持有线程在获得锁之后 和释放锁之前这段时间所执行的代码为临界区(Critical Section)
锁具有排他性(Exclusive),即一个锁只能被一个线程使用,这种锁被称为排它锁或者互斥锁(Mutex)
JVM把锁分为内部所和显示锁两种。内部锁通过synchronized关键字实现;显示锁通过java.concurrent.locks.Lock接口实现类实现的
1.2锁的作用
锁可以实现对共享数据的安全访问,保障线程的原子性,可见性与有序性
锁是通过互斥保障原子性,一个锁只能被一个线程持有,这就保证临界区的代码一次只能被一次线程执行。使得临界区的代码所执行的操作自然而然的具有不可分割的特性,即具备了原子性
可见性的保障是通过写线程冲刷处理器的缓存和对线程刷新处理器缓存这两个动作实现的。在java平台中,锁的获得隐含着刷新处理器缓存的动作,锁的释放隐含着冲刷处理器的动作。
锁能够保障有序性,写线程在临界区所执行的在读线程锁执行的临界区看来像是完全按照原码顺序执行的,
注意:
使用锁保障线程的安全性,必须满足以下条件:
- 这些数据在访问共享数据是必须使用同一个锁
- 即使是读线程共享数据的线程也需要使用同步锁
1.3 锁相关的概念
-
可重如性(Reentrncy)描述一个问题:一个线程持有该锁的时候能再次(多次)申请该锁 如果一个线程持有一个锁的时候还能够继续成功申请该锁,称该锁是重入的,否则就成该锁为不可重入的
-
锁的争用与调度 java平台中内部锁属于非公平锁,显示Lock锁即支持公平锁又支持非公平锁
-
锁的粒度 一个锁可以保护的共享数据数量大小称为锁的粒度 锁保护共享数据量大,称该锁的粒度粗,否则就称该锁的粒度细 锁的粒度过粗就会导致线程在申请锁时会进行不必要的等待,锁的粒度过细会增加锁调度的开销