线程的生命周期
一共有6中状态:NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED
Synchronized的作用范围
- 给普通方法加synchronized :只会在类的实例中进行锁控制,不是全局锁
public class SyncTest{ public synchronized void test(){…}},
- 给代码块加synchronized:只会在类的实例中进行锁控制,不是全局锁
public class SyncTest{ public void test(){synchronized(this){}}}
- 给代码块加synchronized:全局锁,所有的实例共同竞争一个锁
public class SyncTest{ public void test(){synchronized(SyncTest.class){}}}
- 给静态方法加synchronized:全局锁,所有的实例共同竞争一个锁
public class SyncTest{public synchronized static void test(){…}}
Volatile关键字
volatile的作用
volatile可以使得在多处理器环境下保证了共享变量的可见性,那么到底什么是可见性呢?
什么叫缓存一致性呢?
首先,有了高速缓存的存在以后,每个CPU的处理过程是,先将计算需要用到的数据缓存在CPU高速缓存中,在CPU进行计算时,直接从高速缓存中读取数据并且在计算完成之后写入到缓存中。在整个运算过程完成后,再把缓存中的数据同步到主内存。由于在多CPU种,每个线程可能会运行在不同的CPU内,并且每个线程拥有自己的高速缓存。同一份数据可能会被缓存到多个CPU中,如果在不同CPU中运行的不同线程看到同一份内存的缓存值不一样就会存在缓存不一致的问题。CPU通过一些操作实现了可控的缓存保持一致。
为了达到数据访问的一致,需要各个处理器在访问缓存时遵循一些协议,在读写时根据协议来操作,常见的协议有MSI,MESI,MOSI等。最常见的就是MESI协议。
缓存一致性的解决方案
主要提供了两种解决办法
- 总线锁
- 缓存锁
总线锁:
简单来说就是,在多cpu下,当其中一个处理器要对共享内存进行操作的时候,在总线上发出一个LOCK#信号,这个信号使得其他处理器无法通过总线来访问到共享内存中的数据,总线锁定把CPU和内存之间的通信锁住了,这使得锁定期间,其他处理器不能操作其他内存地址的数据,所以总线锁定的开销比较大,这种机制显然是不合适的
缓存锁:
最好的方法就是控制锁的保护粒度,我们只需要保证对于被多个CPU缓存的同一份数据是一致的就行。所以引入了缓存锁,它核心机制是基于缓存一致性协议来实现的。