第二章 对象及变量的并发访问

时间:2020-12-01 16:08:29

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关键字修改之后,还是会存在并发的情况。