synchronized与volatile多线程中的作用
注明:文中内容总结来自书籍《Java多线程编程核心技术》第二章
关键字synchronized
- 同步
两个线程访问同一个对象中的同步方法时一定是线程安全的。
synchronized 修饰的方法一定是排队运行的,只有共享资源的读写访问才需要同步化,否则不需要。
- dirtyRead 脏读:
出现在读取实例变量时,该变量已经被其他线程修改。而解决脏读的一种方法就是在读取实例变量的方法加上synchronized关键字。这样,当线程A访问该实例的方法A(synchronized修饰)时,线程B如果需要访问该实例方法B(synchronized修饰),必须等线程A执行完方法A,释放该方法所在的对象锁。
需要留意的是,当线程A访问实例的synchronized方法时,并不影响线程B访问该实例对象的非synchronized方式。也就是说,线程之间的同步的方法只是同一实例对象的synchronized方法。
- synchronized锁重入
自己可以再次获取自己的内部锁。也就是说,当线程A获取获取了某个对象的锁,当这个对象锁还没有释放的时候,线程A需要再次获取这个对象锁的时候是可以获取到的,不然就会造成死锁。可重入锁支持父子类继承(子类可以通过“可重入锁”调用父类的同步方法)。
- 出现异常,锁会自动释放
- 父类的synchronizd修饰的方法,并不会被子类继承。子类如果仍需要继承的方法是同步的,需要加上synchronized关键字
- synchronized同步代码块
- synchronized(this)锁定当前对象
- synchronized(非this对象)锁定 对象监视器必须为同一个,不然仍然是异步调用
- 静态同步synchronized方法与synchronized(class)代码块
- 两者持有Class锁等同,但与持有对象锁是不一样的。
- 数据类型String的常量池
- 因为JVM中具有String常量池缓存的功能,所以讲synchronized(String)同步快与String联合使用时,要注意常量池带来的意外。因为在常量池中两个String “AA” 其实是同一个对象,所以锁对象也是同一个。
- 同步synchronized代码块都不适用String作为锁对象。
关键字volatile
- 变量在多个线程可见,强制从公共堆栈中取值,不从线程私有数据栈中取值,但是却不能保证原子性。
- volatile解决的是变量读时的可见性问题,但无法保证原子性,对于多个线程访问同一个实例变量还是需要加锁同步。
线程安全包含原子性和可见性两个方面,Java的同步机制都是围绕这两个方面来确保线程安全的。
关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或者某个代码块。包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。
外练互斥,内修可见