参考链接:
http://tutorials.jenkov.com/java-concurrency/synchronized.html
http://tutorials.jenkov.com/java-concurrency/volatile.html
https://blog.csdn.net/jdbdh/article/details/81839225
https://blog.csdn.net/jdbdh/article/details/81774240
https://blog.csdn.net/jdbdh/article/details/81780764
学习小结
1、Synchronized
Synchronized用来声明同步块,一般的形式如下:
synchronized(object){
...
}
同步代码块由两部分组成:一个为锁的对象引用(上述代码的object)和一个由这个锁保护的代码块(大括号内的代码)。将进入该代码块的线程会尝试获取锁,如果改对象没有被加锁,则该线程便获得锁并执行同步块,而其他线程尝试对对象加锁的线程都会被阻塞。不管是不是进入同一代码块,只要锁对象相同且被加锁,其他线程就必须等待。
同步块也有其他方便的写法,就是直接将synchronized直接声明在方法上,那么锁对象为拥有该方法的对象,代码块为整个方法的代码块。比如声明在对象方法上时,锁为该对象。如果声明在静态方法上,那么该方法属于类的,而类也是一个对象(类Class的对象),因此锁为该类。
2、Volatile
一般cpu从内存读时,会先将一块数据读入到cpu缓存中,然后读入缓存。写数据时,cpu会写入到cpu缓存,而缓存何时被写入到主存是不清楚的。而volatile则强制cpu每次访问volatile变量时都直接从内存中访问,而写入数据时,直接写入到主存中。
3、可见性
volatile保证了可见性很容易理解,因为不同线程被强制对内存操作了,而不是对缓存操作,就不会出现不同地方数据不一致的问题,共享数据只有一份,因此保证了不同线程对同一共享变量的可见性。
那Synchronized是如果做到的呢?因为同步块一次只有一个线程执行,因此可以规定:同步块开始执行时,将共享变量从内存中读入到cpu缓存,之后的操作可以在缓存中进行,同步块结束时将数据写回到内存中。这样下一个线程从内存中读入的数据永远是最新的,保证了数据的一致性。因此保证了不同线程对同一共享变量的可见性。
4、原子性
只有同步块才能保证原子性,volatile不能保证。所谓原子性就是同步块的代码同一时刻只能由同一个线程执行。这个和数据库事务处理的原子性不同,事务处理的原子性是指多条sql语句要么全部执行完,要么全部不执行。事务处理的原子性还是会存在并发问题,导致脏读等问题。