原子性、可见性、有序性
原子性:一段指令不会被拆分给多个处理器执行
synchronized能实现原子性
可见性:数据被修改后,其他线程立即得知这个变化
volatile、synchronized和final都能实现可见性
有序性:编译器处理字节码时,会做重排序,避免重排序就是有序性
volatile、synchronized能实现有序性
Synchronized
实例对象/方法锁
synchronized一般就是锁一个代码块或锁一个方法:
锁代码块synchronized(this) //当前类实例对象
锁方法public synchronized void test(){}
在实例锁中,其实锁的对象都是类的实例对象
类锁(静态类/方法锁)
与实例对象/方法锁的区别在于,静态类和静态方法,都是在方法区的唯一内存,相应的锁也是唯一的。
实例对象锁和类锁是两个不同的锁,互不干扰,甚至可以同时获取。
内置锁
每个java对象可以作为一个锁,也就是内置锁,这样在同步时不需要显式的创建锁对象
所以,synchronized只是个内置锁的加锁机制,某个方法加上了synchronized后,线程就必须获取内置锁后才能执行,同时,其他线程,可以访问未加锁的对象和方法。
对代码块加锁的优势
synchronized同步会阻塞其他线程,这是性能上的巨大损耗,对方法加锁的话,锁对象就是实例对象,不可优化。
但是对代码块加锁的话,一方面可以尽早释放锁;另一方面可以传不同的锁对象,
java synchronized关键字的用法
Lock(和ReentrantLock)
synchronized其实是jvm提供的同步机制,而lock是依赖硬件cpu指令实现的。
所以,用lock实现同步,需要自己实现复杂的代码,lock、unlock成对出现,并确保在finally中unlock。
另外,synchronized是悲观锁,必须阻塞其他线程;而lock可以实现乐观锁,不阻塞,失败重试,消耗低。
可重入锁Reentrantlock是lock的一个子类,有三处改进:可以锁多个对象、可以主动退出等待、可以按请求的先后顺序分配锁。
Volatile
每个工作线程都有自己的工作内存,工作内存的数据来自于主内存,并写回到主内存。
Volatile是易变的,它的机制就是对数据的修改立即写回主内存,在使用数据时再立即从主内存取一次数据(因为是易变的),实际就是依靠主内存来实现可见性。
不过,volatile在运算时数据仍然可能不是最新的,因为一行java代码,最终形成的机器码可能有多行,在前面拿到的volatile数据,在后面真正操作时,可能已经不是最新的了。
Final
只在类的加载连接阶段写一次,然后不再修改,所以满足可见性,就是说,它通过不修改满足了修改结果可见。
sleep和wait/notify
sleep仍持有锁,wait释放了锁,要等别的线程notify通知才能去尝试拿回锁