当累加操作少于百万次时,单线程执行的速度会比多线程执行的速度快,因为线程有创建和上下文切换的开销
vmstat的cs表示每秒上下文切换的次数
使用无锁并发编程,CAS算法,使用最少线程和使用协程
死锁样例:
public class DeadLockDemo { /** A锁 */ private static String A = "A"; /** B锁 */ private static String B = "B"; public static void main(String[] args) { new DeadLockDemo().deadLock(); } private void deadLock() { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (A) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (B) { System.out.println("1"); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (B) { synchronized (A) { System.out.println("2"); } } } }); t1.start(); t2.start(); } }
现实场景中,比如t1拿到锁之后,因为一些异常情况没有释放锁(死循环)。又或者是t1拿到一个数据库锁,释放锁的时候抛出了异常,没释放掉,就会出现死锁
volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性
可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值
java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。
Java语言提供了volatile,在某些情况下比锁要更加方便。如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的
Lock前缀指令会引起处理器缓存回写到内存
一个处理器的缓存回写到内存会导致其他处理器的缓存无效
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步
代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的
无锁,偏向锁,轻量级锁,重量级锁
锁可以升级不能降级,目的是为了提高获得锁和释放锁的效率
public class UnsafeLazyInitialization { private static Instance instance; public static Instance getInstance() { if (instance == null) // 1:A线程执行 instance = new Instance(); // 2:B线程执行 return instance; } static class Instance { } }
这样初始化对象时会出现指令重排,导致取得instance对象时没有初始化完成
解决办法有两个,一个是禁止指令重排序,二是让指令重排序不让外面知道
1.instance类加上volatile关键字
2.利用JVM在类的初始化阶段(在class被加载后,且被线程使用之前),会执行类的初始化
在执行类的初始化期间,jvm会获取一个锁,这个锁可以同步多个线程对同一个类的初始化