注明:文中内容总结来自书籍《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不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。

外练互斥,内修可见