1.Synchronized
关键字
a.synchronized加在方法的前部
public synchronized void synMethod() {
//方法体
}
调用用关键字synchronized声明的方法是排队运行的。但假如线程A持有某对象的锁,那线程B异步调用非synchronized类型的方法不受限制。
b. synchronized同步代码块
synchronized(this)
c.将任意对象作为监视器
synchronized(非this对象)
d.静态同步
(1)synchronize(class)
(2)在静态方法前面加上synchronized关键字
小结:
a.synchronized取得的锁都是对象锁,而不是把一段代码或方法当作锁,在线程的执行过程当中,哪个线程先执行了带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁,那么其他线程只能等待,前提是多个线程访问的是同一个对象的synchronized方法。如果多个线程访问多个对象,则JVM会创建多个锁。
b.如果一个线程已经持有了一个对象的锁,另外一个线程则不能访问该对象的同步方法,但是可以访问该对象的非同步方法。
c.同步不具有继承性,如果父类的某个方法拥有synchronized关键字,子类的该方法若想同步必须手动的加入synchronized关键字;
d.关键字加到static方法上是给Class上锁,加到非static是给对象上锁。
class锁可以对类的所有实例起作用,即就是在任何时候,只能有一个线程访问该类的static方法。因为static方法就是类方法。
Class锁 锁住以后,以实例对象为参数的线程可以获取到实例对象中的方法。比如User.Class锁住后,以user1 user2 user3 为参数的线程还是能获取到相应对象的锁。可以理解为锁对象是按照地址来判断锁住的是不是同一个对象。
线程的私有堆栈
2. volatile关键字
主要作用是使变量在多个线程间可见。加volatile关键字可强制性从公共堆栈进行取值,而不是从线程私有数据栈中取得变量的值。
小陷阱
将JVM设置为-server模式,为了线程运行的效率,会在线程的私有堆栈中取值,在不使用volatile关键字的时候,如果对共有堆栈中的值进行改变,会造成值的不统一。
读取公共内存图
Atomic
相关类
一个原子类型就是一个原子操作可用的类型,可在没有锁的情况下做到线程安全。但原子类也不是完全安全,虽然原子操作是安全的,可方法间的调用却不是原子的,需要用同步。
synchronized和volatile比较
1)关键字volatile是线程同步的轻量级实现,性能比synchronized好,且volatile只能修饰变量,synchronized可修饰方法和代码块。
2)多线程访问volatile不会发生阻塞,synchronized会出现阻塞
3)volatile能保证数据可见性,不保证原子性;synchronized可以保证原子性,也可以间接保证可见性,因为synchronized会将私有内存和公共内存中的数据做同步。
4)volatile解决的是变量在多个线程间的可见性,synchronized解决的是多个线程访问资源的同步性。
volatile
不具备同步性,也不具备原子性
如图:
read and load 从主存复制变量到当前工作内存
use and assign 执行代码,改变共享变量值
store and write 用工作内存数据刷新主存相关内容
其中use and assign 可以多次出现
但是这一些操作并不是原子性,也就是 在readload之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样
对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的
例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值
在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6
线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6
导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。